aboutsummaryrefslogtreecommitdiff
path: root/gdbserver/tracepoint.cc
diff options
context:
space:
mode:
authorSimon Marchi <simon.marchi@efficios.com>2020-02-13 16:27:51 -0500
committerSimon Marchi <simon.marchi@efficios.com>2020-02-13 16:27:51 -0500
commitfeacfcacaac9f7e62f467a33c4ae54c56501ed18 (patch)
tree0e083366350e9fd279bec8c7b12413c90b9f255d /gdbserver/tracepoint.cc
parent06b3c5bdb018262d7c09cda9d4637015c7ebe779 (diff)
downloadgdb-feacfcacaac9f7e62f467a33c4ae54c56501ed18.zip
gdb-feacfcacaac9f7e62f467a33c4ae54c56501ed18.tar.gz
gdb-feacfcacaac9f7e62f467a33c4ae54c56501ed18.tar.bz2
gdbserver: rename source files to .cc
For the same reasons outlined in the previous patch, this patch renames gdbserver source files to .cc. I have moved the "-x c++" switch to only those rules that require it. gdbserver/ChangeLog: * Makefile.in: Rename source files from .c to .cc. * %.c: Rename to %.cc. * configure.ac: Rename server.c to server.cc. * configure: Re-generate.
Diffstat (limited to 'gdbserver/tracepoint.cc')
-rw-r--r--gdbserver/tracepoint.cc7473
1 files changed, 7473 insertions, 0 deletions
diff --git a/gdbserver/tracepoint.cc b/gdbserver/tracepoint.cc
new file mode 100644
index 0000000..bbca48b
--- /dev/null
+++ b/gdbserver/tracepoint.cc
@@ -0,0 +1,7473 @@
+/* Tracepoint code for remote server for GDB.
+ Copyright (C) 2009-2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "tracepoint.h"
+#include "gdbthread.h"
+#include "gdbsupport/rsp-low.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <chrono>
+#include <inttypes.h>
+#include "ax.h"
+#include "tdesc.h"
+
+#define IPA_SYM_STRUCT_NAME ipa_sym_addresses
+#include "gdbsupport/agent.h"
+
+#define DEFAULT_TRACE_BUFFER_SIZE 5242880 /* 5*1024*1024 */
+
+/* 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
+ implemented by patching the tracepoint location with a jump into a
+ small trampoline function whose job is to save the register state,
+ call the in-process tracing agent, and then execute the original
+ instruction that was under the tracepoint jump (possibly adjusted,
+ if PC-relative, or some such).
+
+ The current synchronization design is pull based. That means,
+ GDBserver does most of the work, by peeking/poking at the inferior
+ agent's memory directly for downloading tracepoint and associated
+ objects, and for uploading trace frames. Whenever the IPA needs
+ something from GDBserver (trace buffer is full, tracing stopped for
+ some reason, etc.) the IPA calls a corresponding hook function
+ where GDBserver has placed a breakpoint.
+
+ Each of the agents has its own trace buffer. When browsing the
+ trace frames built from slow and fast tracepoints from GDB (tfind
+ mode), there's no guarantee the user is seeing the trace frames in
+ strict chronological creation order, although, GDBserver tries to
+ keep the order relatively reasonable, by syncing the trace buffers
+ at appropriate times.
+
+*/
+
+#ifdef IN_PROCESS_AGENT
+
+static void trace_vdebug (const char *, ...) ATTRIBUTE_PRINTF (1, 2);
+
+static void
+trace_vdebug (const char *fmt, ...)
+{
+ char buf[1024];
+ va_list ap;
+
+ va_start (ap, fmt);
+ vsprintf (buf, fmt, ap);
+ fprintf (stderr, PROG "/tracepoint: %s\n", buf);
+ va_end (ap);
+}
+
+#define trace_debug_1(level, fmt, args...) \
+ do { \
+ if (level <= debug_threads) \
+ trace_vdebug ((fmt), ##args); \
+ } while (0)
+
+#else
+
+#define trace_debug_1(level, fmt, args...) \
+ do { \
+ if (level <= debug_threads) \
+ { \
+ debug_printf ((fmt), ##args); \
+ debug_printf ("\n"); \
+ } \
+ } while (0)
+
+#endif
+
+#define trace_debug(FMT, args...) \
+ trace_debug_1 (1, FMT, ##args)
+
+/* Prefix exported symbols, for good citizenship. All the symbols
+ that need exporting are defined in this module. Note that all
+ these symbols must be tagged with IP_AGENT_EXPORT_*. */
+#ifdef IN_PROCESS_AGENT
+# define gdb_tp_heap_buffer IPA_SYM_EXPORTED_NAME (gdb_tp_heap_buffer)
+# define gdb_jump_pad_buffer IPA_SYM_EXPORTED_NAME (gdb_jump_pad_buffer)
+# define gdb_jump_pad_buffer_end IPA_SYM_EXPORTED_NAME (gdb_jump_pad_buffer_end)
+# define gdb_trampoline_buffer IPA_SYM_EXPORTED_NAME (gdb_trampoline_buffer)
+# define gdb_trampoline_buffer_end IPA_SYM_EXPORTED_NAME (gdb_trampoline_buffer_end)
+# define gdb_trampoline_buffer_error IPA_SYM_EXPORTED_NAME (gdb_trampoline_buffer_error)
+# define collecting IPA_SYM_EXPORTED_NAME (collecting)
+# define gdb_collect_ptr IPA_SYM_EXPORTED_NAME (gdb_collect_ptr)
+# define stop_tracing IPA_SYM_EXPORTED_NAME (stop_tracing)
+# define flush_trace_buffer IPA_SYM_EXPORTED_NAME (flush_trace_buffer)
+# define about_to_request_buffer_space IPA_SYM_EXPORTED_NAME (about_to_request_buffer_space)
+# define trace_buffer_is_full IPA_SYM_EXPORTED_NAME (trace_buffer_is_full)
+# define stopping_tracepoint IPA_SYM_EXPORTED_NAME (stopping_tracepoint)
+# define expr_eval_result IPA_SYM_EXPORTED_NAME (expr_eval_result)
+# define error_tracepoint IPA_SYM_EXPORTED_NAME (error_tracepoint)
+# define tracepoints IPA_SYM_EXPORTED_NAME (tracepoints)
+# define tracing IPA_SYM_EXPORTED_NAME (tracing)
+# define trace_buffer_ctrl IPA_SYM_EXPORTED_NAME (trace_buffer_ctrl)
+# define trace_buffer_ctrl_curr IPA_SYM_EXPORTED_NAME (trace_buffer_ctrl_curr)
+# define trace_buffer_lo IPA_SYM_EXPORTED_NAME (trace_buffer_lo)
+# define trace_buffer_hi IPA_SYM_EXPORTED_NAME (trace_buffer_hi)
+# define traceframe_read_count IPA_SYM_EXPORTED_NAME (traceframe_read_count)
+# define traceframe_write_count IPA_SYM_EXPORTED_NAME (traceframe_write_count)
+# define traceframes_created IPA_SYM_EXPORTED_NAME (traceframes_created)
+# define trace_state_variables IPA_SYM_EXPORTED_NAME (trace_state_variables)
+# define get_raw_reg_ptr IPA_SYM_EXPORTED_NAME (get_raw_reg_ptr)
+# define get_trace_state_variable_value_ptr \
+ IPA_SYM_EXPORTED_NAME (get_trace_state_variable_value_ptr)
+# define set_trace_state_variable_value_ptr \
+ IPA_SYM_EXPORTED_NAME (set_trace_state_variable_value_ptr)
+# define ust_loaded IPA_SYM_EXPORTED_NAME (ust_loaded)
+# define helper_thread_id IPA_SYM_EXPORTED_NAME (helper_thread_id)
+# define cmd_buf IPA_SYM_EXPORTED_NAME (cmd_buf)
+# define ipa_tdesc_idx IPA_SYM_EXPORTED_NAME (ipa_tdesc_idx)
+#endif
+
+#ifndef IN_PROCESS_AGENT
+
+/* Addresses of in-process agent's symbols GDBserver cares about. */
+
+struct ipa_sym_addresses
+{
+ 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_ptr;
+ CORE_ADDR addr_stop_tracing;
+ CORE_ADDR addr_flush_trace_buffer;
+ CORE_ADDR addr_about_to_request_buffer_space;
+ CORE_ADDR addr_trace_buffer_is_full;
+ CORE_ADDR addr_stopping_tracepoint;
+ CORE_ADDR addr_expr_eval_result;
+ CORE_ADDR addr_error_tracepoint;
+ CORE_ADDR addr_tracepoints;
+ CORE_ADDR addr_tracing;
+ CORE_ADDR addr_trace_buffer_ctrl;
+ CORE_ADDR addr_trace_buffer_ctrl_curr;
+ CORE_ADDR addr_trace_buffer_lo;
+ CORE_ADDR addr_trace_buffer_hi;
+ CORE_ADDR addr_traceframe_read_count;
+ CORE_ADDR addr_traceframe_write_count;
+ CORE_ADDR addr_traceframes_created;
+ CORE_ADDR addr_trace_state_variables;
+ CORE_ADDR addr_get_raw_reg_ptr;
+ CORE_ADDR addr_get_trace_state_variable_value_ptr;
+ CORE_ADDR addr_set_trace_state_variable_value_ptr;
+ CORE_ADDR addr_ust_loaded;
+ CORE_ADDR addr_ipa_tdesc_idx;
+};
+
+static struct
+{
+ const char *name;
+ int offset;
+} symbol_list[] = {
+ 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_ptr),
+ IPA_SYM(stop_tracing),
+ IPA_SYM(flush_trace_buffer),
+ IPA_SYM(about_to_request_buffer_space),
+ IPA_SYM(trace_buffer_is_full),
+ IPA_SYM(stopping_tracepoint),
+ IPA_SYM(expr_eval_result),
+ IPA_SYM(error_tracepoint),
+ IPA_SYM(tracepoints),
+ IPA_SYM(tracing),
+ IPA_SYM(trace_buffer_ctrl),
+ IPA_SYM(trace_buffer_ctrl_curr),
+ IPA_SYM(trace_buffer_lo),
+ IPA_SYM(trace_buffer_hi),
+ IPA_SYM(traceframe_read_count),
+ IPA_SYM(traceframe_write_count),
+ IPA_SYM(traceframes_created),
+ IPA_SYM(trace_state_variables),
+ IPA_SYM(get_raw_reg_ptr),
+ IPA_SYM(get_trace_state_variable_value_ptr),
+ IPA_SYM(set_trace_state_variable_value_ptr),
+ IPA_SYM(ust_loaded),
+ IPA_SYM(ipa_tdesc_idx),
+};
+
+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, and agent has
+ capability on static tracepoints. */
+
+static int
+in_process_agent_supports_ust (void)
+{
+ int loaded = 0;
+
+ if (!agent_loaded_p ())
+ {
+ warning ("In-process agent not loaded");
+ return 0;
+ }
+
+ if (agent_capability_check (AGENT_CAPA_STATIC_TRACE))
+ {
+ /* 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;
+ }
+ else
+ return 0;
+}
+
+static void
+write_e_ipa_not_loaded (char *buffer)
+{
+ sprintf (buffer,
+ "E.In-process agent library not loaded in process. "
+ "Fast and static tracepoints unavailable.");
+}
+
+/* Write an error to BUFFER indicating that UST isn't loaded in the
+ inferior. */
+
+static void
+write_e_ust_not_loaded (char *buffer)
+{
+#ifdef HAVE_UST
+ sprintf (buffer,
+ "E.UST library not loaded in process. "
+ "Static tracepoints unavailable.");
+#else
+ sprintf (buffer, "E.GDBserver was built without static tracepoints support");
+#endif
+}
+
+/* If the in-process agent library isn't loaded in the inferior, write
+ an error to BUFFER, and return 1. Otherwise, return 0. */
+
+static int
+maybe_write_ipa_not_loaded (char *buffer)
+{
+ if (!agent_loaded_p ())
+ {
+ write_e_ipa_not_loaded (buffer);
+ return 1;
+ }
+ return 0;
+}
+
+/* If the in-process agent library and the ust (static tracepoints)
+ library aren't loaded in the inferior, write an error to BUFFER,
+ and return 1. Otherwise, return 0. */
+
+static int
+maybe_write_ipa_ust_not_loaded (char *buffer)
+{
+ if (!agent_loaded_p ())
+ {
+ write_e_ipa_not_loaded (buffer);
+ return 1;
+ }
+ else if (!in_process_agent_supports_ust ())
+ {
+ write_e_ust_not_loaded (buffer);
+ return 1;
+ }
+ return 0;
+}
+
+/* Cache all future symbols that the tracepoints module might request.
+ We can not request symbols at arbitrary states in the remote
+ protocol, only when the client tells us that new symbols are
+ available. So when we load the in-process library, make sure to
+ check the entire list. */
+
+void
+tracepoint_look_up_symbols (void)
+{
+ int i;
+
+ if (agent_loaded_p ())
+ return;
+
+ for (i = 0; i < sizeof (symbol_list) / sizeof (symbol_list[0]); i++)
+ {
+ CORE_ADDR *addrp =
+ (CORE_ADDR *) ((char *) &ipa_sym_addrs + symbol_list[i].offset);
+
+ if (look_up_one_symbol (symbol_list[i].name, addrp, 1) == 0)
+ {
+ if (debug_threads)
+ debug_printf ("symbol `%s' not found\n", symbol_list[i].name);
+ return;
+ }
+ }
+
+ agent_look_up_symbols (NULL);
+}
+
+#endif
+
+/* GDBserver places a breakpoint on the IPA's version (which is a nop)
+ of the "stop_tracing" function. When this breakpoint is hit,
+ tracing stopped in the IPA for some reason. E.g., due to
+ tracepoint reaching the pass count, hitting conditional expression
+ evaluation error, etc.
+
+ The IPA's trace buffer is never in circular tracing mode: instead,
+ GDBserver's is, and whenever the in-process buffer fills, it calls
+ "flush_trace_buffer", which triggers an internal breakpoint.
+ GDBserver reacts to this breakpoint by pulling the meanwhile
+ collected data. Old frames discarding is always handled on the
+ GDBserver side. */
+
+#ifdef IN_PROCESS_AGENT
+int
+read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+ memcpy (myaddr, (void *) (uintptr_t) memaddr, len);
+ return 0;
+}
+
+/* Call this in the functions where GDBserver places a breakpoint, so
+ that the compiler doesn't try to be clever and skip calling the
+ function at all. This is necessary, even if we tell the compiler
+ to not inline said functions. */
+
+#if defined(__GNUC__)
+# define UNKNOWN_SIDE_EFFECTS() asm ("")
+#else
+# define UNKNOWN_SIDE_EFFECTS() do {} while (0)
+#endif
+
+/* This is needed for -Wmissing-declarations. */
+IP_AGENT_EXPORT_FUNC void stop_tracing (void);
+
+IP_AGENT_EXPORT_FUNC void
+stop_tracing (void)
+{
+ /* GDBserver places breakpoint here. */
+ UNKNOWN_SIDE_EFFECTS();
+}
+
+/* This is needed for -Wmissing-declarations. */
+IP_AGENT_EXPORT_FUNC void flush_trace_buffer (void);
+
+IP_AGENT_EXPORT_FUNC void
+flush_trace_buffer (void)
+{
+ /* GDBserver places breakpoint here. */
+ UNKNOWN_SIDE_EFFECTS();
+}
+
+#endif
+
+#ifndef IN_PROCESS_AGENT
+static int
+tracepoint_handler (CORE_ADDR address)
+{
+ trace_debug ("tracepoint_handler: tracepoint at 0x%s hit",
+ paddress (address));
+ return 0;
+}
+
+/* Breakpoint at "stop_tracing" in the inferior lib. */
+struct breakpoint *stop_tracing_bkpt;
+static int stop_tracing_handler (CORE_ADDR);
+
+/* Breakpoint at "flush_trace_buffer" in the inferior lib. */
+struct breakpoint *flush_trace_buffer_bkpt;
+static int flush_trace_buffer_handler (CORE_ADDR);
+
+static void download_trace_state_variables (void);
+static void upload_fast_traceframes (void);
+
+static int run_inferior_command (char *cmd, int len);
+
+static int
+read_inferior_integer (CORE_ADDR symaddr, int *val)
+{
+ return read_inferior_memory (symaddr, (unsigned char *) 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 read_inferior_memory (symaddr, (unsigned char *) val,
+ sizeof (*val));
+}
+
+static int
+read_inferior_data_pointer (CORE_ADDR symaddr, CORE_ADDR *val)
+{
+ void *pval = (void *) (uintptr_t) val;
+ int ret;
+
+ ret = read_inferior_memory (symaddr, (unsigned char *) &pval, sizeof (pval));
+ *val = (uintptr_t) pval;
+ return ret;
+}
+
+static int
+write_inferior_data_pointer (CORE_ADDR symaddr, CORE_ADDR val)
+{
+ void *pval = (void *) (uintptr_t) val;
+ return target_write_memory (symaddr,
+ (unsigned char *) &pval, sizeof (pval));
+}
+
+static int
+write_inferior_integer (CORE_ADDR symaddr, int val)
+{
+ return target_write_memory (symaddr, (unsigned char *) &val, sizeof (val));
+}
+
+static int
+write_inferior_int8 (CORE_ADDR symaddr, int8_t val)
+{
+ return target_write_memory (symaddr, (unsigned char *) &val, sizeof (val));
+}
+
+static int
+write_inferior_uinteger (CORE_ADDR symaddr, unsigned int val)
+{
+ return target_write_memory (symaddr, (unsigned char *) &val, sizeof (val));
+}
+
+static CORE_ADDR target_malloc (ULONGEST size);
+
+#define COPY_FIELD_TO_BUF(BUF, OBJ, FIELD) \
+ do { \
+ memcpy (BUF, &(OBJ)->FIELD, sizeof ((OBJ)->FIELD)); \
+ BUF += sizeof ((OBJ)->FIELD); \
+ } while (0)
+
+#endif
+
+/* Base action. Concrete actions inherit this. */
+
+struct tracepoint_action
+{
+ char type;
+};
+
+/* An 'M' (collect memory) action. */
+struct collect_memory_action
+{
+ struct tracepoint_action base;
+
+ ULONGEST addr;
+ ULONGEST len;
+ int32_t basereg;
+};
+
+/* An 'R' (collect registers) action. */
+
+struct collect_registers_action
+{
+ struct tracepoint_action base;
+};
+
+/* An 'X' (evaluate expression) action. */
+
+struct eval_expr_action
+{
+ struct tracepoint_action base;
+
+ struct agent_expr *expr;
+};
+
+/* An 'L' (collect static trace data) action. */
+struct collect_static_trace_data_action
+{
+ struct tracepoint_action base;
+};
+
+#ifndef IN_PROCESS_AGENT
+static CORE_ADDR
+m_tracepoint_action_download (const struct tracepoint_action *action)
+{
+ CORE_ADDR ipa_action = target_malloc (sizeof (struct collect_memory_action));
+
+ target_write_memory (ipa_action, (unsigned char *) action,
+ sizeof (struct collect_memory_action));
+
+ 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 CORE_ADDR
+r_tracepoint_action_download (const struct tracepoint_action *action)
+{
+ CORE_ADDR ipa_action = target_malloc (sizeof (struct collect_registers_action));
+
+ target_write_memory (ipa_action, (unsigned char *) action,
+ sizeof (struct collect_registers_action));
+
+ return ipa_action;
+}
+
+static char *
+r_tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
+{
+ return buffer;
+}
+
+static CORE_ADDR download_agent_expr (struct agent_expr *expr);
+
+static CORE_ADDR
+x_tracepoint_action_download (const struct tracepoint_action *action)
+{
+ CORE_ADDR ipa_action = target_malloc (sizeof (struct eval_expr_action));
+ CORE_ADDR expr;
+
+ target_write_memory (ipa_action, (unsigned char *) action,
+ sizeof (struct eval_expr_action));
+ expr = download_agent_expr (((struct eval_expr_action *) action)->expr);
+ write_inferior_data_pointer (ipa_action
+ + offsetof (struct eval_expr_action, expr),
+ 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 CORE_ADDR
+l_tracepoint_action_download (const struct tracepoint_action *action)
+{
+ CORE_ADDR ipa_action
+ = target_malloc (sizeof (struct collect_static_trace_data_action));
+
+ target_write_memory (ipa_action, (unsigned char *) action,
+ sizeof (struct collect_static_trace_data_action));
+
+ return ipa_action;
+}
+
+static char *
+l_tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
+{
+ return buffer;
+}
+
+static char *
+tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
+{
+ switch (action->type)
+ {
+ case 'M':
+ return m_tracepoint_action_send (buffer, action);
+ case 'R':
+ return r_tracepoint_action_send (buffer, action);
+ case 'X':
+ return x_tracepoint_action_send (buffer, action);
+ case 'L':
+ return l_tracepoint_action_send (buffer, action);
+ }
+ error ("Unknown trace action '%c'.", action->type);
+}
+
+static CORE_ADDR
+tracepoint_action_download (const struct tracepoint_action *action)
+{
+ switch (action->type)
+ {
+ case 'M':
+ return m_tracepoint_action_download (action);
+ case 'R':
+ return r_tracepoint_action_download (action);
+ case 'X':
+ return x_tracepoint_action_download (action);
+ case 'L':
+ return l_tracepoint_action_download (action);
+ }
+ error ("Unknown trace action '%c'.", action->type);
+}
+#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. */
+
+struct source_string
+{
+ /* The type of string, such as "cond" for a conditional. */
+ char *type;
+
+ /* The source-level string itself. For the sake of target
+ debugging, we store it in plaintext, even though it is always
+ transmitted in hex. */
+ char *str;
+
+ /* Link to the next one in the list. We link them in the order
+ received, in case some make up an ordered list of commands or
+ some such. */
+ struct source_string *next;
+};
+
+enum tracepoint_type
+{
+ /* Trap based tracepoint. */
+ trap_tracepoint,
+
+ /* A fast tracepoint implemented with a jump instead of a trap. */
+ fast_tracepoint,
+
+ /* A static tracepoint, implemented by a program call into a tracing
+ library. */
+ static_tracepoint
+};
+
+struct tracepoint_hit_ctx;
+
+typedef enum eval_result_type (*condfn) (unsigned char *,
+ ULONGEST *);
+
+/* The definition of a tracepoint. */
+
+/* Tracepoints may have multiple locations, each at a different
+ address. This can occur with optimizations, template
+ instantiation, etc. Since the locations may be in different
+ scopes, the conditions and actions may be different for each
+ location. Our target version of tracepoints is more like GDB's
+ notion of "breakpoint locations", but we have almost nothing that
+ is not per-location, so we bother having two kinds of objects. The
+ key consequence is that numbers are not unique, and that it takes
+ both number and address to identify a tracepoint uniquely. */
+
+struct tracepoint
+{
+ /* The number of the tracepoint, as specified by GDB. Several
+ tracepoint objects here may share a number. */
+ uint32_t number;
+
+ /* Address at which the tracepoint is supposed to trigger. Several
+ tracepoints may share an address. */
+ CORE_ADDR address;
+
+ /* Tracepoint type. */
+ enum tracepoint_type type;
+
+ /* True if the tracepoint is currently enabled. */
+ int8_t enabled;
+
+ /* The number of single steps that will be performed after each
+ tracepoint hit. */
+ uint64_t step_count;
+
+ /* The number of times the tracepoint may be hit before it will
+ terminate the entire tracing run. */
+ 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. */
+ 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". */
+ uint64_t hit_count;
+
+ /* Cached sum of the sizes of traceframes created by this point. */
+ uint64_t traceframe_usage;
+
+ CORE_ADDR compiled_cond;
+
+ /* Link to the next tracepoint in the list. */
+ struct tracepoint *next;
+
+#ifndef IN_PROCESS_AGENT
+ /* The list of actions to take when the tracepoint triggers, in
+ string/packet form. */
+ char **actions_str;
+
+ /* The collection of strings that describe the tracepoint as it was
+ entered into GDB. These are not used by the target, but are
+ reported back to GDB upon reconnection. */
+ struct source_string *source_strings;
+
+ /* 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. */
+ uint32_t orig_size;
+
+ /* Only for fast tracepoints. */
+ CORE_ADDR obj_addr_on_target;
+
+ /* Address range where the original instruction under a fast
+ tracepoint was relocated to. (_end is actually one byte past
+ the end). */
+ CORE_ADDR adjusted_insn_addr;
+ CORE_ADDR adjusted_insn_addr_end;
+
+ /* The address range of the piece of the jump pad buffer that was
+ assigned to this fast tracepoint. (_end is actually one byte
+ past the end).*/
+ 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;
+ struct tracepoint_action **step_actions;
+ /* Same, but in string/packet form. */
+ char **step_actions_str;
+
+ /* Handle returned by the breakpoint or tracepoint module when we
+ inserted the trap or jump, or hooked into a static tracepoint.
+ NULL if we haven't inserted it yet. */
+ void *handle;
+#endif
+
+};
+
+#ifndef IN_PROCESS_AGENT
+
+/* Given `while-stepping', a thread may be collecting data for more
+ than one tracepoint simultaneously. On the other hand, the same
+ tracepoint with a while-stepping action may be hit by more than one
+ thread simultaneously (but not quite, each thread could be handling
+ a different step). Each thread holds a list of these objects,
+ representing the current step of each while-stepping action being
+ collected. */
+
+struct wstep_state
+{
+ struct wstep_state *next;
+
+ /* The tracepoint number. */
+ int tp_number;
+ /* The tracepoint's address. */
+ CORE_ADDR tp_address;
+
+ /* The number of the current step in this 'while-stepping'
+ action. */
+ long current_step;
+};
+
+#endif
+
+EXTERN_C_PUSH
+
+/* The linked list of all tracepoints. Marked explicitly as used as
+ the in-process library doesn't use it for the fast tracepoints
+ support. */
+IP_AGENT_EXPORT_VAR struct tracepoint *tracepoints;
+
+/* The first tracepoint to exceed its pass count. */
+
+IP_AGENT_EXPORT_VAR struct tracepoint *stopping_tracepoint;
+
+/* True if the trace buffer is full or otherwise no longer usable. */
+
+IP_AGENT_EXPORT_VAR int trace_buffer_is_full;
+
+/* The first error that occurred during expression evaluation. */
+
+/* Stored as an int to avoid the IPA ABI being dependent on whatever
+ the compiler decides to use for the enum's underlying type. Holds
+ enum eval_result_type values. */
+IP_AGENT_EXPORT_VAR int expr_eval_result = expr_eval_no_error;
+
+EXTERN_C_POP
+
+#ifndef IN_PROCESS_AGENT
+
+/* Pointer to the last tracepoint in the list, new tracepoints are
+ linked in at the end. */
+
+static struct tracepoint *last_tracepoint;
+
+static const char *eval_result_names[] =
+ {
+ "terror:in the attic", /* this should never be reported */
+ "terror:empty expression",
+ "terror:empty stack",
+ "terror:stack overflow",
+ "terror:stack underflow",
+ "terror:unhandled opcode",
+ "terror:unrecognized opcode",
+ "terror:divide by zero"
+ };
+
+#endif
+
+/* The tracepoint in which the error occurred. */
+
+EXTERN_C_PUSH
+IP_AGENT_EXPORT_VAR struct tracepoint *error_tracepoint;
+EXTERN_C_POP
+
+struct trace_state_variable
+{
+ /* This is the name of the variable as used in GDB. The target
+ doesn't use the name, but needs to have it for saving and
+ reconnection purposes. */
+ char *name;
+
+ /* This number identifies the variable uniquely. Numbers may be
+ assigned either by the target (in the case of builtin variables),
+ or by GDB, and are presumed unique during the course of a trace
+ experiment. */
+ int number;
+
+ /* The variable's initial value, a 64-bit signed integer always. */
+ LONGEST initial_value;
+
+ /* The variable's value, a 64-bit signed integer always. */
+ LONGEST value;
+
+ /* Pointer to a getter function, used to supply computed values. */
+ LONGEST (*getter) (void);
+
+ /* Link to the next variable. */
+ struct trace_state_variable *next;
+};
+
+/* Linked list of all trace state variables. */
+
+#ifdef IN_PROCESS_AGENT
+struct trace_state_variable *alloced_trace_state_variables;
+#endif
+
+IP_AGENT_EXPORT_VAR struct trace_state_variable *trace_state_variables;
+
+/* The results of tracing go into a fixed-size space known as the
+ "trace buffer". Because usage follows a limited number of
+ patterns, we manage it ourselves rather than with malloc. Basic
+ rules are that we create only one trace frame at a time, each is
+ variable in size, they are never moved once created, and we only
+ discard if we are doing a circular buffer, and then only the oldest
+ ones. Each trace frame includes its own size, so we don't need to
+ link them together, and the trace frame number is relative to the
+ first one, so we don't need to record numbers. A trace frame also
+ records the number of the tracepoint that created it. The data
+ itself is a series of blocks, each introduced by a single character
+ and with a defined format. Each type of block has enough
+ type/length info to allow scanners to jump quickly from one block
+ to the next without reading each byte in the block. */
+
+/* Trace buffer management would be simple - advance a free pointer
+ from beginning to end, then stop - were it not for the circular
+ buffer option, which is a useful way to prevent a trace run from
+ stopping prematurely because the buffer filled up. In the circular
+ case, the location of the first trace frame (trace_buffer_start)
+ moves as old trace frames are discarded. Also, since we grow trace
+ frames incrementally as actions are performed, we wrap around to
+ the beginning of the trace buffer. This is per-block, so each
+ block within a trace frame remains contiguous. Things get messy
+ when the wrapped-around trace frame is the one being discarded; the
+ free space ends up in two parts at opposite ends of the buffer. */
+
+#ifndef ATTR_PACKED
+# if defined(__GNUC__)
+# define ATTR_PACKED __attribute__ ((packed))
+# else
+# define ATTR_PACKED /* nothing */
+# endif
+#endif
+
+/* The data collected at a tracepoint hit. This object should be as
+ small as possible, since there may be a great many of them. We do
+ not need to keep a frame number, because they are all sequential
+ and there are no deletions; so the Nth frame in the buffer is
+ always frame number N. */
+
+struct traceframe
+{
+ /* Number of the tracepoint that collected this traceframe. A value
+ of 0 indicates the current end of the trace buffer. We make this
+ a 16-bit field because it's never going to happen that GDB's
+ numbering of tracepoints reaches 32,000. */
+ int tpnum : 16;
+
+ /* The size of the data in this trace frame. We limit this to 32
+ bits, even on a 64-bit target, because it's just implausible that
+ one is validly going to collect 4 gigabytes of data at a single
+ tracepoint hit. */
+ unsigned int data_size : 32;
+
+ /* The base of the trace data, which is contiguous from this point. */
+ unsigned char data[0];
+
+} ATTR_PACKED;
+
+/* The size of the EOB marker, in bytes. A traceframe with zeroed
+ fields (and no data) marks the end of trace data. */
+#define TRACEFRAME_EOB_MARKER_SIZE offsetof (struct traceframe, data)
+
+/* This flag is true if the trace buffer is circular, meaning that
+ when it fills, the oldest trace frames are discarded in order to
+ make room. */
+
+#ifndef IN_PROCESS_AGENT
+static int circular_trace_buffer;
+#endif
+
+/* Size of the trace buffer. */
+
+static LONGEST trace_buffer_size;
+
+EXTERN_C_PUSH
+
+/* Pointer to the block of memory that traceframes all go into. */
+
+IP_AGENT_EXPORT_VAR unsigned char *trace_buffer_lo;
+
+/* Pointer to the end of the trace buffer, more precisely to the byte
+ after the end of the buffer. */
+
+IP_AGENT_EXPORT_VAR unsigned char *trace_buffer_hi;
+
+EXTERN_C_POP
+
+/* Control structure holding the read/write/etc. pointers into the
+ trace buffer. We need more than one of these to implement a
+ transaction-like mechanism to guarantees that both GDBserver and the
+ in-process agent can try to change the trace buffer
+ simultaneously. */
+
+struct trace_buffer_control
+{
+ /* Pointer to the first trace frame in the buffer. In the
+ non-circular case, this is equal to trace_buffer_lo, otherwise it
+ moves around in the buffer. */
+ unsigned char *start;
+
+ /* Pointer to the free part of the trace buffer. Note that we clear
+ several bytes at and after this pointer, so that traceframe
+ scans/searches terminate properly. */
+ unsigned char *free;
+
+ /* Pointer to the byte after the end of the free part. Note that
+ this may be smaller than trace_buffer_free in the circular case,
+ and means that the free part is in two pieces. Initially it is
+ equal to trace_buffer_hi, then is generally equivalent to
+ trace_buffer_start. */
+ unsigned char *end_free;
+
+ /* Pointer to the wraparound. If not equal to trace_buffer_hi, then
+ this is the point at which the trace data breaks, and resumes at
+ trace_buffer_lo. */
+ unsigned char *wrap;
+};
+
+/* Same as above, to be used by GDBserver when updating the in-process
+ agent. */
+struct ipa_trace_buffer_control
+{
+ uintptr_t start;
+ uintptr_t free;
+ uintptr_t end_free;
+ uintptr_t wrap;
+};
+
+
+/* We have possibly both GDBserver and an inferior thread accessing
+ the same IPA trace buffer memory. The IPA is the producer (tries
+ to put new frames in the buffer), while GDBserver occasionally
+ consumes them, that is, flushes the IPA's buffer into its own
+ buffer. Both sides need to update the trace buffer control
+ pointers (current head, tail, etc.). We can't use a global lock to
+ synchronize the accesses, as otherwise we could deadlock GDBserver
+ (if the thread holding the lock stops for a signal, say). So
+ instead of that, we use a transaction scheme where GDBserver writes
+ always prevail over the IPAs writes, and, we have the IPA detect
+ the commit failure/overwrite, and retry the whole attempt. This is
+ mainly implemented by having a global token object that represents
+ who wrote last to the buffer control structure. We need to freeze
+ any inferior writing to the buffer while GDBserver touches memory,
+ so that the inferior can correctly detect that GDBserver had been
+ there, otherwise, it could mistakingly think its commit was
+ successful; that's implemented by simply having GDBserver set a
+ breakpoint the inferior hits if it is the critical region.
+
+ There are three cycling trace buffer control structure copies
+ (buffer head, tail, etc.), with the token object including an index
+ indicating which is current live copy. The IPA tentatively builds
+ an updated copy in a non-current control structure, while GDBserver
+ always clobbers the current version directly. The IPA then tries
+ to atomically "commit" its version; if GDBserver clobbered the
+ structure meanwhile, that will fail, and the IPA restarts the
+ allocation process.
+
+ Listing the step in further detail, we have:
+
+ In-process agent (producer):
+
+ - passes by `about_to_request_buffer_space' breakpoint/lock
+
+ - reads current token, extracts current trace buffer control index,
+ and starts tentatively updating the rightmost one (0->1, 1->2,
+ 2->0). Note that only one inferior thread is executing this code
+ at any given time, due to an outer lock in the jump pads.
+
+ - updates counters, and tries to commit the token.
+
+ - passes by second `about_to_request_buffer_space' breakpoint/lock,
+ leaving the sync region.
+
+ - checks if the update was effective.
+
+ - if trace buffer was found full, hits flush_trace_buffer
+ breakpoint, and restarts later afterwards.
+
+ GDBserver (consumer):
+
+ - sets `about_to_request_buffer_space' breakpoint/lock.
+
+ - updates the token unconditionally, using the current buffer
+ control index, since it knows that the IP agent always writes to
+ the rightmost, and due to the breakpoint, at most one IP thread
+ can try to update the trace buffer concurrently to GDBserver, so
+ there will be no danger of trace buffer control index wrap making
+ the IPA write to the same index as GDBserver.
+
+ - flushes the IP agent's trace buffer completely, and updates the
+ current trace buffer control structure. GDBserver *always* wins.
+
+ - removes the `about_to_request_buffer_space' breakpoint.
+
+The token is stored in the `trace_buffer_ctrl_curr' variable.
+Internally, it's bits are defined as:
+
+ |-------------+-----+-------------+--------+-------------+--------------|
+ | Bit offsets | 31 | 30 - 20 | 19 | 18-8 | 7-0 |
+ |-------------+-----+-------------+--------+-------------+--------------|
+ | What | GSB | PC (11-bit) | unused | CC (11-bit) | TBCI (8-bit) |
+ |-------------+-----+-------------+--------+-------------+--------------|
+
+ GSB - GDBserver Stamp Bit
+ PC - Previous Counter
+ CC - Current Counter
+ TBCI - Trace Buffer Control Index
+
+
+An IPA update of `trace_buffer_ctrl_curr' does:
+
+ - read CC from the current token, save as PC.
+ - updates pointers
+ - atomically tries to write PC+1,CC
+
+A GDBserver update of `trace_buffer_ctrl_curr' does:
+
+ - reads PC and CC from the current token.
+ - updates pointers
+ - writes GSB,PC,CC
+*/
+
+/* These are the bits of `trace_buffer_ctrl_curr' that are reserved
+ for the counters described below. The cleared bits are used to
+ hold the index of the items of the `trace_buffer_ctrl' array that
+ is "current". */
+#define GDBSERVER_FLUSH_COUNT_MASK 0xfffffff0
+
+/* `trace_buffer_ctrl_curr' contains two counters. The `previous'
+ counter, and the `current' counter. */
+
+#define GDBSERVER_FLUSH_COUNT_MASK_PREV 0x7ff00000
+#define GDBSERVER_FLUSH_COUNT_MASK_CURR 0x0007ff00
+
+/* When GDBserver update the IP agent's `trace_buffer_ctrl_curr', it
+ always stamps this bit as set. */
+#define GDBSERVER_UPDATED_FLUSH_COUNT_BIT 0x80000000
+
+#ifdef IN_PROCESS_AGENT
+IP_AGENT_EXPORT_VAR struct trace_buffer_control trace_buffer_ctrl[3];
+IP_AGENT_EXPORT_VAR unsigned int trace_buffer_ctrl_curr;
+
+# define TRACE_BUFFER_CTRL_CURR \
+ (trace_buffer_ctrl_curr & ~GDBSERVER_FLUSH_COUNT_MASK)
+
+#else
+
+/* The GDBserver side agent only needs one instance of this object, as
+ it doesn't need to sync with itself. Define it as array anyway so
+ that the rest of the code base doesn't need to care for the
+ difference. */
+struct trace_buffer_control trace_buffer_ctrl[1];
+# define TRACE_BUFFER_CTRL_CURR 0
+#endif
+
+/* These are convenience macros used to access the current trace
+ buffer control in effect. */
+#define trace_buffer_start (trace_buffer_ctrl[TRACE_BUFFER_CTRL_CURR].start)
+#define trace_buffer_free (trace_buffer_ctrl[TRACE_BUFFER_CTRL_CURR].free)
+#define trace_buffer_end_free \
+ (trace_buffer_ctrl[TRACE_BUFFER_CTRL_CURR].end_free)
+#define trace_buffer_wrap (trace_buffer_ctrl[TRACE_BUFFER_CTRL_CURR].wrap)
+
+
+/* Macro that returns a pointer to the first traceframe in the buffer. */
+
+#define FIRST_TRACEFRAME() ((struct traceframe *) trace_buffer_start)
+
+/* Macro that returns a pointer to the next traceframe in the buffer.
+ If the computed location is beyond the wraparound point, subtract
+ the offset of the wraparound. */
+
+#define NEXT_TRACEFRAME_1(TF) \
+ (((unsigned char *) (TF)) + sizeof (struct traceframe) + (TF)->data_size)
+
+#define NEXT_TRACEFRAME(TF) \
+ ((struct traceframe *) (NEXT_TRACEFRAME_1 (TF) \
+ - ((NEXT_TRACEFRAME_1 (TF) >= trace_buffer_wrap) \
+ ? (trace_buffer_wrap - trace_buffer_lo) \
+ : 0)))
+
+/* The difference between these counters represents the total number
+ of complete traceframes present in the trace buffer. The IP agent
+ writes to the write count, GDBserver writes to read count. */
+
+IP_AGENT_EXPORT_VAR unsigned int traceframe_write_count;
+IP_AGENT_EXPORT_VAR unsigned int traceframe_read_count;
+
+/* Convenience macro. */
+
+#define traceframe_count \
+ ((unsigned int) (traceframe_write_count - traceframe_read_count))
+
+/* The count of all traceframes created in the current run, including
+ ones that were discarded to make room. */
+
+IP_AGENT_EXPORT_VAR int traceframes_created;
+
+#ifndef IN_PROCESS_AGENT
+
+/* Read-only regions are address ranges whose contents don't change,
+ and so can be read from target memory even while looking at a trace
+ frame. Without these, disassembly for instance will likely fail,
+ because the program code is not usually collected into a trace
+ frame. This data structure does not need to be very complicated or
+ particularly efficient, it's only going to be used occasionally,
+ and only by some commands. */
+
+struct readonly_region
+{
+ /* The bounds of the region. */
+ CORE_ADDR start, end;
+
+ /* Link to the next one. */
+ struct readonly_region *next;
+};
+
+/* Linked list of readonly regions. This list stays in effect from
+ one tstart to the next. */
+
+static struct readonly_region *readonly_regions;
+
+#endif
+
+/* The global that controls tracing overall. */
+
+IP_AGENT_EXPORT_VAR int tracing;
+
+#ifndef IN_PROCESS_AGENT
+
+/* Controls whether tracing should continue after GDB disconnects. */
+
+int disconnected_tracing;
+
+/* The reason for the last tracing run to have stopped. We initialize
+ to a distinct string so that GDB can distinguish between "stopped
+ after running" and "stopped because never run" cases. */
+
+static const char *tracing_stop_reason = "tnotrun";
+
+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. */
+
+/* Base "class" for tracepoint type specific data to be passed down to
+ collect_data_at_tracepoint. */
+struct tracepoint_hit_ctx
+{
+ enum tracepoint_type type;
+};
+
+#ifdef IN_PROCESS_AGENT
+
+/* Fast/jump tracepoint specific data to be passed down to
+ collect_data_at_tracepoint. */
+struct fast_tracepoint_ctx
+{
+ struct tracepoint_hit_ctx base;
+
+ struct regcache regcache;
+ int regcache_initted;
+ unsigned char *regspace;
+
+ unsigned char *regs;
+ struct tracepoint *tpoint;
+};
+
+/* Static tracepoint specific data to be passed down to
+ collect_data_at_tracepoint. */
+struct static_tracepoint_ctx
+{
+ struct tracepoint_hit_ctx base;
+
+ /* The regcache corresponding to the registers state at the time of
+ the tracepoint hit. Initialized lazily, from REGS. */
+ struct regcache regcache;
+ int regcache_initted;
+
+ /* The buffer space REGCACHE above uses. We use a separate buffer
+ instead of letting the regcache malloc for both signal safety and
+ performance reasons; this is allocated on the stack instead. */
+ unsigned char *regspace;
+
+ /* The register buffer as passed on by lttng/ust. */
+ struct registers *regs;
+
+ /* The "printf" formatter and the args the user passed to the marker
+ call. We use this to be able to collect "static trace data"
+ ($_sdata). */
+ const char *fmt;
+ va_list *args;
+
+ /* The GDB tracepoint matching the probed marker that was "hit". */
+ struct tracepoint *tpoint;
+};
+
+#else
+
+/* Static tracepoint specific data to be passed down to
+ collect_data_at_tracepoint. */
+struct trap_tracepoint_ctx
+{
+ struct tracepoint_hit_ctx base;
+
+ struct regcache *regcache;
+};
+
+#endif
+
+#ifndef IN_PROCESS_AGENT
+static CORE_ADDR traceframe_get_pc (struct traceframe *tframe);
+static int traceframe_read_tsv (int num, LONGEST *val);
+#endif
+
+static int condition_true_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ struct tracepoint *tpoint);
+
+#ifndef IN_PROCESS_AGENT
+static void clear_readonly_regions (void);
+static void clear_installed_tracepoints (void);
+#endif
+
+static void collect_data_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ CORE_ADDR stop_pc,
+ struct tracepoint *tpoint);
+#ifndef IN_PROCESS_AGENT
+static void collect_data_at_step (struct tracepoint_hit_ctx *ctx,
+ CORE_ADDR stop_pc,
+ struct tracepoint *tpoint, int current_step);
+static void compile_tracepoint_condition (struct tracepoint *tpoint,
+ CORE_ADDR *jump_entry);
+#endif
+static void do_action_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ CORE_ADDR stop_pc,
+ struct tracepoint *tpoint,
+ struct traceframe *tframe,
+ struct tracepoint_action *taction);
+
+#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 memory_barrier() do {} while (0)
+#endif
+
+/* We only build the IPA if this builtin is supported, and there are
+ no uses of this in GDBserver itself, so we're safe in defining this
+ unconditionally. */
+#define cmpxchg(mem, oldval, newval) \
+ __sync_val_compare_and_swap (mem, oldval, newval)
+
+/* Record that an error occurred during expression evaluation. */
+
+static void
+record_tracepoint_error (struct tracepoint *tpoint, const char *which,
+ enum eval_result_type rtype)
+{
+ trace_debug ("Tracepoint %d at %s %s eval reports error %d",
+ tpoint->number, paddress (tpoint->address), which, rtype);
+
+#ifdef IN_PROCESS_AGENT
+ /* Only record the first error we get. */
+ if (cmpxchg (&expr_eval_result,
+ expr_eval_no_error,
+ rtype) != expr_eval_no_error)
+ return;
+#else
+ if (expr_eval_result != expr_eval_no_error)
+ return;
+#endif
+
+ error_tracepoint = tpoint;
+}
+
+/* Trace buffer management. */
+
+static void
+clear_trace_buffer (void)
+{
+ trace_buffer_start = trace_buffer_lo;
+ trace_buffer_free = trace_buffer_lo;
+ trace_buffer_end_free = trace_buffer_hi;
+ trace_buffer_wrap = trace_buffer_hi;
+ /* A traceframe with zeroed fields marks the end of trace data. */
+ ((struct traceframe *) trace_buffer_free)->tpnum = 0;
+ ((struct traceframe *) trace_buffer_free)->data_size = 0;
+ traceframe_read_count = traceframe_write_count = 0;
+ traceframes_created = 0;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+static void
+clear_inferior_trace_buffer (void)
+{
+ CORE_ADDR ipa_trace_buffer_lo;
+ CORE_ADDR ipa_trace_buffer_hi;
+ struct traceframe ipa_traceframe = { 0 };
+ struct ipa_trace_buffer_control ipa_trace_buffer_ctrl;
+
+ read_inferior_data_pointer (ipa_sym_addrs.addr_trace_buffer_lo,
+ &ipa_trace_buffer_lo);
+ read_inferior_data_pointer (ipa_sym_addrs.addr_trace_buffer_hi,
+ &ipa_trace_buffer_hi);
+
+ ipa_trace_buffer_ctrl.start = ipa_trace_buffer_lo;
+ ipa_trace_buffer_ctrl.free = ipa_trace_buffer_lo;
+ ipa_trace_buffer_ctrl.end_free = ipa_trace_buffer_hi;
+ ipa_trace_buffer_ctrl.wrap = ipa_trace_buffer_hi;
+
+ /* A traceframe with zeroed fields marks the end of trace data. */
+ target_write_memory (ipa_sym_addrs.addr_trace_buffer_ctrl,
+ (unsigned char *) &ipa_trace_buffer_ctrl,
+ sizeof (ipa_trace_buffer_ctrl));
+
+ write_inferior_uinteger (ipa_sym_addrs.addr_trace_buffer_ctrl_curr, 0);
+
+ /* A traceframe with zeroed fields marks the end of trace data. */
+ target_write_memory (ipa_trace_buffer_lo,
+ (unsigned char *) &ipa_traceframe,
+ sizeof (ipa_traceframe));
+
+ write_inferior_uinteger (ipa_sym_addrs.addr_traceframe_write_count, 0);
+ write_inferior_uinteger (ipa_sym_addrs.addr_traceframe_read_count, 0);
+ write_inferior_integer (ipa_sym_addrs.addr_traceframes_created, 0);
+}
+
+#endif
+
+static void
+init_trace_buffer (LONGEST bufsize)
+{
+ size_t alloc_size;
+
+ trace_buffer_size = bufsize;
+
+ /* Make sure to internally allocate at least space for the EOB
+ marker. */
+ alloc_size = (bufsize < TRACEFRAME_EOB_MARKER_SIZE
+ ? TRACEFRAME_EOB_MARKER_SIZE : bufsize);
+ trace_buffer_lo = (unsigned char *) xrealloc (trace_buffer_lo, alloc_size);
+
+ trace_buffer_hi = trace_buffer_lo + trace_buffer_size;
+
+ clear_trace_buffer ();
+}
+
+#ifdef IN_PROCESS_AGENT
+
+/* This is needed for -Wmissing-declarations. */
+IP_AGENT_EXPORT_FUNC void about_to_request_buffer_space (void);
+
+IP_AGENT_EXPORT_FUNC void
+about_to_request_buffer_space (void)
+{
+ /* GDBserver places breakpoint here while it goes about to flush
+ data at random times. */
+ UNKNOWN_SIDE_EFFECTS();
+}
+
+#endif
+
+/* Carve out a piece of the trace buffer, returning NULL in case of
+ failure. */
+
+static void *
+trace_buffer_alloc (size_t amt)
+{
+ unsigned char *rslt;
+ struct trace_buffer_control *tbctrl;
+ unsigned int curr;
+#ifdef IN_PROCESS_AGENT
+ unsigned int prev, prev_filtered;
+ unsigned int commit_count;
+ unsigned int commit;
+ unsigned int readout;
+#else
+ struct traceframe *oldest;
+ unsigned char *new_start;
+#endif
+
+ trace_debug ("Want to allocate %ld+%ld bytes in trace buffer",
+ (long) amt, (long) sizeof (struct traceframe));
+
+ /* Account for the EOB marker. */
+ amt += TRACEFRAME_EOB_MARKER_SIZE;
+
+#ifdef IN_PROCESS_AGENT
+ again:
+ memory_barrier ();
+
+ /* Read the current token and extract the index to try to write to,
+ storing it in CURR. */
+ prev = trace_buffer_ctrl_curr;
+ prev_filtered = prev & ~GDBSERVER_FLUSH_COUNT_MASK;
+ curr = prev_filtered + 1;
+ if (curr > 2)
+ curr = 0;
+
+ about_to_request_buffer_space ();
+
+ /* Start out with a copy of the current state. GDBserver may be
+ midway writing to the PREV_FILTERED TBC, but, that's OK, we won't
+ be able to commit anyway if that happens. */
+ trace_buffer_ctrl[curr]
+ = trace_buffer_ctrl[prev_filtered];
+ trace_debug ("trying curr=%u", curr);
+#else
+ /* The GDBserver's agent doesn't need all that syncing, and always
+ updates TCB 0 (there's only one, mind you). */
+ curr = 0;
+#endif
+ tbctrl = &trace_buffer_ctrl[curr];
+
+ /* Offsets are easier to grok for debugging than raw addresses,
+ especially for the small trace buffer sizes that are useful for
+ testing. */
+ trace_debug ("Trace buffer [%d] start=%d free=%d endfree=%d wrap=%d hi=%d",
+ curr,
+ (int) (tbctrl->start - trace_buffer_lo),
+ (int) (tbctrl->free - trace_buffer_lo),
+ (int) (tbctrl->end_free - trace_buffer_lo),
+ (int) (tbctrl->wrap - trace_buffer_lo),
+ (int) (trace_buffer_hi - trace_buffer_lo));
+
+ /* The algorithm here is to keep trying to get a contiguous block of
+ the requested size, possibly discarding older traceframes to free
+ up space. Since free space might come in one or two pieces,
+ depending on whether discarded traceframes wrapped around at the
+ high end of the buffer, we test both pieces after each
+ discard. */
+ while (1)
+ {
+ /* First, if we have two free parts, try the upper one first. */
+ if (tbctrl->end_free < tbctrl->free)
+ {
+ if (tbctrl->free + amt <= trace_buffer_hi)
+ /* We have enough in the upper part. */
+ break;
+ else
+ {
+ /* Our high part of free space wasn't enough. Give up
+ on it for now, set wraparound. We will recover the
+ space later, if/when the wrapped-around traceframe is
+ discarded. */
+ trace_debug ("Upper part too small, setting wraparound");
+ tbctrl->wrap = tbctrl->free;
+ tbctrl->free = trace_buffer_lo;
+ }
+ }
+
+ /* The normal case. */
+ if (tbctrl->free + amt <= tbctrl->end_free)
+ break;
+
+#ifdef IN_PROCESS_AGENT
+ /* The IP Agent's buffer is always circular. It isn't used
+ currently, but `circular_trace_buffer' could represent
+ GDBserver's mode. If we didn't find space, ask GDBserver to
+ flush. */
+
+ flush_trace_buffer ();
+ memory_barrier ();
+ if (tracing)
+ {
+ trace_debug ("gdbserver flushed buffer, retrying");
+ goto again;
+ }
+
+ /* GDBserver cancelled the tracing. Bail out as well. */
+ return NULL;
+#else
+ /* If we're here, then neither part is big enough, and
+ non-circular trace buffers are now full. */
+ if (!circular_trace_buffer)
+ {
+ trace_debug ("Not enough space in the trace buffer");
+ return NULL;
+ }
+
+ trace_debug ("Need more space in the trace buffer");
+
+ /* If we have a circular buffer, we can try discarding the
+ oldest traceframe and see if that helps. */
+ oldest = FIRST_TRACEFRAME ();
+ if (oldest->tpnum == 0)
+ {
+ /* Not good; we have no traceframes to free. Perhaps we're
+ asking for a block that is larger than the buffer? In
+ any case, give up. */
+ trace_debug ("No traceframes to discard");
+ return NULL;
+ }
+
+ /* We don't run this code in the in-process agent currently.
+ E.g., we could leave the in-process agent in autonomous
+ circular mode if we only have fast tracepoints. If we do
+ that, then this bit becomes racy with GDBserver, which also
+ writes to this counter. */
+ --traceframe_write_count;
+
+ new_start = (unsigned char *) NEXT_TRACEFRAME (oldest);
+ /* If we freed the traceframe that wrapped around, go back
+ to the non-wrap case. */
+ if (new_start < tbctrl->start)
+ {
+ trace_debug ("Discarding past the wraparound");
+ tbctrl->wrap = trace_buffer_hi;
+ }
+ tbctrl->start = new_start;
+ tbctrl->end_free = tbctrl->start;
+
+ trace_debug ("Discarded a traceframe\n"
+ "Trace buffer [%d], start=%d free=%d "
+ "endfree=%d wrap=%d hi=%d",
+ curr,
+ (int) (tbctrl->start - trace_buffer_lo),
+ (int) (tbctrl->free - trace_buffer_lo),
+ (int) (tbctrl->end_free - trace_buffer_lo),
+ (int) (tbctrl->wrap - trace_buffer_lo),
+ (int) (trace_buffer_hi - trace_buffer_lo));
+
+ /* Now go back around the loop. The discard might have resulted
+ in either one or two pieces of free space, so we want to try
+ both before freeing any more traceframes. */
+#endif
+ }
+
+ /* If we get here, we know we can provide the asked-for space. */
+
+ rslt = tbctrl->free;
+
+ /* Adjust the request back down, now that we know we have space for
+ the marker, but don't commit to AMT yet, we may still need to
+ restart the operation if GDBserver touches the trace buffer
+ (obviously only important in the in-process agent's version). */
+ tbctrl->free += (amt - sizeof (struct traceframe));
+
+ /* Or not. If GDBserver changed the trace buffer behind our back,
+ we get to restart a new allocation attempt. */
+
+#ifdef IN_PROCESS_AGENT
+ /* Build the tentative token. */
+ 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);
+
+ /* Try to commit it. */
+ readout = cmpxchg (&trace_buffer_ctrl_curr, prev, commit);
+ if (readout != prev)
+ {
+ trace_debug ("GDBserver has touched the trace buffer, restarting."
+ " (prev=%08x, commit=%08x, readout=%08x)",
+ prev, commit, readout);
+ goto again;
+ }
+
+ /* Hold your horses here. Even if that change was committed,
+ GDBserver could come in, and clobber it. We need to hold to be
+ able to tell if GDBserver clobbers before or after we committed
+ the change. Whenever GDBserver goes about touching the IPA
+ buffer, it sets a breakpoint in this routine, so we have a sync
+ point here. */
+ about_to_request_buffer_space ();
+
+ /* Check if the change has been effective, even if GDBserver stopped
+ us at the breakpoint. */
+
+ {
+ unsigned int refetch;
+
+ memory_barrier ();
+
+ refetch = trace_buffer_ctrl_curr;
+
+ if (refetch == commit
+ || ((refetch & GDBSERVER_FLUSH_COUNT_MASK_PREV) >> 12) == commit_count)
+ {
+ /* effective */
+ trace_debug ("change is effective: (prev=%08x, commit=%08x, "
+ "readout=%08x, refetch=%08x)",
+ prev, commit, readout, refetch);
+ }
+ else
+ {
+ trace_debug ("GDBserver has touched the trace buffer, not effective."
+ " (prev=%08x, commit=%08x, readout=%08x, refetch=%08x)",
+ prev, commit, readout, refetch);
+ goto again;
+ }
+ }
+#endif
+
+ /* We have a new piece of the trace buffer. Hurray! */
+
+ /* Add an EOB marker just past this allocation. */
+ ((struct traceframe *) tbctrl->free)->tpnum = 0;
+ ((struct traceframe *) tbctrl->free)->data_size = 0;
+
+ /* Adjust the request back down, now that we know we have space for
+ the marker. */
+ amt -= sizeof (struct traceframe);
+
+ if (debug_threads)
+ {
+ trace_debug ("Allocated %d bytes", (int) amt);
+ trace_debug ("Trace buffer [%d] start=%d free=%d "
+ "endfree=%d wrap=%d hi=%d",
+ curr,
+ (int) (tbctrl->start - trace_buffer_lo),
+ (int) (tbctrl->free - trace_buffer_lo),
+ (int) (tbctrl->end_free - trace_buffer_lo),
+ (int) (tbctrl->wrap - trace_buffer_lo),
+ (int) (trace_buffer_hi - trace_buffer_lo));
+ }
+
+ return rslt;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+/* Return the total free space. This is not necessarily the largest
+ block we can allocate, because of the two-part case. */
+
+static int
+free_space (void)
+{
+ if (trace_buffer_free <= trace_buffer_end_free)
+ return trace_buffer_end_free - trace_buffer_free;
+ else
+ return ((trace_buffer_end_free - trace_buffer_lo)
+ + (trace_buffer_hi - trace_buffer_free));
+}
+
+/* An 'S' in continuation packets indicates remainder are for
+ while-stepping. */
+
+static int seen_step_action_flag;
+
+/* 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, **tp_next;
+
+ tpoint = XNEW (struct tracepoint);
+ tpoint->number = num;
+ tpoint->address = addr;
+ tpoint->numactions = 0;
+ tpoint->actions = NULL;
+ tpoint->actions_str = NULL;
+ tpoint->cond = NULL;
+ tpoint->num_step_actions = 0;
+ tpoint->step_actions = NULL;
+ tpoint->step_actions_str = NULL;
+ /* Start all off as regular (slow) tracepoints. */
+ tpoint->type = trap_tracepoint;
+ tpoint->orig_size = -1;
+ tpoint->source_strings = NULL;
+ tpoint->compiled_cond = 0;
+ tpoint->handle = NULL;
+ tpoint->next = NULL;
+
+ /* 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 tpoint;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+/* Return the tracepoint with the given number and address, or NULL. */
+
+static struct tracepoint *
+find_tracepoint (int id, CORE_ADDR addr)
+{
+ struct tracepoint *tpoint;
+
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ if (tpoint->number == id && tpoint->address == addr)
+ return tpoint;
+
+ 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
+ first argument is NULL. */
+
+static struct tracepoint *
+find_next_tracepoint_by_number (struct tracepoint *prev_tp, int num)
+{
+ struct tracepoint *tpoint;
+
+ if (prev_tp)
+ tpoint = prev_tp->next;
+ else
+ tpoint = tracepoints;
+ for (; tpoint; tpoint = tpoint->next)
+ if (tpoint->number == num)
+ return tpoint;
+
+ return NULL;
+}
+
+#endif
+
+/* Append another action to perform when the tracepoint triggers. */
+
+static void
+add_tracepoint_action (struct tracepoint *tpoint, const char *packet)
+{
+ const char *act;
+
+ if (*packet == 'S')
+ {
+ seen_step_action_flag = 1;
+ ++packet;
+ }
+
+ act = packet;
+
+ while (*act)
+ {
+ const char *act_start = act;
+ struct tracepoint_action *action = NULL;
+
+ switch (*act)
+ {
+ case 'M':
+ {
+ struct collect_memory_action *maction =
+ XNEW (struct collect_memory_action);
+ ULONGEST basereg;
+ int is_neg;
+
+ maction->base.type = *act;
+ action = &maction->base;
+
+ ++act;
+ is_neg = (*act == '-');
+ if (*act == '-')
+ ++act;
+ act = unpack_varlen_hex (act, &basereg);
+ ++act;
+ act = unpack_varlen_hex (act, &maction->addr);
+ ++act;
+ act = unpack_varlen_hex (act, &maction->len);
+ maction->basereg = (is_neg
+ ? - (int) basereg
+ : (int) basereg);
+ trace_debug ("Want to collect %s bytes at 0x%s (basereg %d)",
+ pulongest (maction->len),
+ paddress (maction->addr), maction->basereg);
+ break;
+ }
+ case 'R':
+ {
+ struct collect_registers_action *raction =
+ XNEW (struct collect_registers_action);
+
+ raction->base.type = *act;
+ action = &raction->base;
+
+ trace_debug ("Want to collect registers");
+ ++act;
+ /* skip past hex digits of mask for now */
+ while (isxdigit(*act))
+ ++act;
+ break;
+ }
+ case 'L':
+ {
+ struct collect_static_trace_data_action *raction =
+ XNEW (struct collect_static_trace_data_action);
+
+ raction->base.type = *act;
+ action = &raction->base;
+
+ trace_debug ("Want to collect static trace data");
+ ++act;
+ break;
+ }
+ case 'S':
+ trace_debug ("Unexpected step action, ignoring");
+ ++act;
+ break;
+ case 'X':
+ {
+ struct eval_expr_action *xaction = XNEW (struct eval_expr_action);
+
+ xaction->base.type = *act;
+ action = &xaction->base;
+
+ trace_debug ("Want to evaluate expression");
+ xaction->expr = gdb_parse_agent_expr (&act);
+ break;
+ }
+ default:
+ trace_debug ("unknown trace action '%c', ignoring...", *act);
+ break;
+ case '-':
+ break;
+ }
+
+ if (action == NULL)
+ break;
+
+ if (seen_step_action_flag)
+ {
+ tpoint->num_step_actions++;
+
+ tpoint->step_actions
+ = XRESIZEVEC (struct tracepoint_action *, tpoint->step_actions,
+ tpoint->num_step_actions);
+ tpoint->step_actions_str
+ = XRESIZEVEC (char *, tpoint->step_actions_str,
+ tpoint->num_step_actions);
+ tpoint->step_actions[tpoint->num_step_actions - 1] = action;
+ tpoint->step_actions_str[tpoint->num_step_actions - 1]
+ = savestring (act_start, act - act_start);
+ }
+ else
+ {
+ tpoint->numactions++;
+ tpoint->actions
+ = XRESIZEVEC (struct tracepoint_action *, tpoint->actions,
+ tpoint->numactions);
+ tpoint->actions_str
+ = XRESIZEVEC (char *, tpoint->actions_str, tpoint->numactions);
+ tpoint->actions[tpoint->numactions - 1] = action;
+ tpoint->actions_str[tpoint->numactions - 1]
+ = savestring (act_start, act - act_start);
+ }
+ }
+}
+
+#endif
+
+/* Find or create a trace state variable with the given number. */
+
+static struct trace_state_variable *
+get_trace_state_variable (int num)
+{
+ struct trace_state_variable *tsv;
+
+#ifdef IN_PROCESS_AGENT
+ /* Search for an existing variable. */
+ for (tsv = alloced_trace_state_variables; tsv; tsv = tsv->next)
+ if (tsv->number == num)
+ return tsv;
+#endif
+
+ /* Search for an existing variable. */
+ for (tsv = trace_state_variables; tsv; tsv = tsv->next)
+ if (tsv->number == num)
+ return tsv;
+
+ return NULL;
+}
+
+/* Find or create a trace state variable with the given number. */
+
+static struct trace_state_variable *
+create_trace_state_variable (int num, int gdb)
+{
+ struct trace_state_variable *tsv;
+
+ tsv = get_trace_state_variable (num);
+ if (tsv != NULL)
+ return tsv;
+
+ /* Create a new variable. */
+ tsv = XNEW (struct trace_state_variable);
+ tsv->number = num;
+ tsv->initial_value = 0;
+ tsv->value = 0;
+ tsv->getter = NULL;
+ tsv->name = NULL;
+#ifdef IN_PROCESS_AGENT
+ if (!gdb)
+ {
+ tsv->next = alloced_trace_state_variables;
+ alloced_trace_state_variables = tsv;
+ }
+ else
+#endif
+ {
+ tsv->next = trace_state_variables;
+ trace_state_variables = tsv;
+ }
+ return tsv;
+}
+
+/* This is needed for -Wmissing-declarations. */
+IP_AGENT_EXPORT_FUNC LONGEST get_trace_state_variable_value (int num);
+
+IP_AGENT_EXPORT_FUNC LONGEST
+get_trace_state_variable_value (int num)
+{
+ struct trace_state_variable *tsv;
+
+ tsv = get_trace_state_variable (num);
+
+ if (!tsv)
+ {
+ trace_debug ("No trace state variable %d, skipping value get", num);
+ return 0;
+ }
+
+ /* Call a getter function if we have one. While it's tempting to
+ set up something to only call the getter once per tracepoint hit,
+ it could run afoul of thread races. Better to let the getter
+ handle it directly, if necessary to worry about it. */
+ if (tsv->getter)
+ tsv->value = (tsv->getter) ();
+
+ trace_debug ("get_trace_state_variable_value(%d) ==> %s",
+ num, plongest (tsv->value));
+
+ return tsv->value;
+}
+
+/* This is needed for -Wmissing-declarations. */
+IP_AGENT_EXPORT_FUNC void set_trace_state_variable_value (int num,
+ LONGEST val);
+
+IP_AGENT_EXPORT_FUNC void
+set_trace_state_variable_value (int num, LONGEST val)
+{
+ struct trace_state_variable *tsv;
+
+ tsv = get_trace_state_variable (num);
+
+ if (!tsv)
+ {
+ trace_debug ("No trace state variable %d, skipping value set", num);
+ return;
+ }
+
+ 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)
+{
+ struct trace_state_variable *tsv;
+
+ tsv = get_trace_state_variable (num);
+
+ if (!tsv)
+ {
+ trace_debug ("No trace state variable %d, skipping name set", num);
+ return;
+ }
+
+ tsv->name = (char *) name;
+}
+
+static void
+set_trace_state_variable_getter (int num, LONGEST (*getter) (void))
+{
+ struct trace_state_variable *tsv;
+
+ tsv = get_trace_state_variable (num);
+
+ if (!tsv)
+ {
+ trace_debug ("No trace state variable %d, skipping getter set", num);
+ return;
+ }
+
+ tsv->getter = getter;
+}
+
+/* Add a raw traceframe for the given tracepoint. */
+
+static struct traceframe *
+add_traceframe (struct tracepoint *tpoint)
+{
+ struct traceframe *tframe;
+
+ tframe
+ = (struct traceframe *) trace_buffer_alloc (sizeof (struct traceframe));
+
+ if (tframe == NULL)
+ return NULL;
+
+ tframe->tpnum = tpoint->number;
+ tframe->data_size = 0;
+
+ return tframe;
+}
+
+/* Add a block to the traceframe currently being worked on. */
+
+static unsigned char *
+add_traceframe_block (struct traceframe *tframe,
+ struct tracepoint *tpoint, int amt)
+{
+ unsigned char *block;
+
+ if (!tframe)
+ return NULL;
+
+ block = (unsigned char *) trace_buffer_alloc (amt);
+
+ if (!block)
+ return NULL;
+
+ gdb_assert (tframe->tpnum == tpoint->number);
+
+ tframe->data_size += amt;
+ tpoint->traceframe_usage += amt;
+
+ return block;
+}
+
+/* Flag that the current traceframe is finished. */
+
+static void
+finish_traceframe (struct traceframe *tframe)
+{
+ ++traceframe_write_count;
+ ++traceframes_created;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+/* Given a traceframe number NUM, find the NUMth traceframe in the
+ buffer. */
+
+static struct traceframe *
+find_traceframe (int num)
+{
+ struct traceframe *tframe;
+ int tfnum = 0;
+
+ for (tframe = FIRST_TRACEFRAME ();
+ tframe->tpnum != 0;
+ tframe = NEXT_TRACEFRAME (tframe))
+ {
+ if (tfnum == num)
+ return tframe;
+ ++tfnum;
+ }
+
+ return NULL;
+}
+
+static CORE_ADDR
+get_traceframe_address (struct traceframe *tframe)
+{
+ CORE_ADDR addr;
+ struct tracepoint *tpoint;
+
+ addr = traceframe_get_pc (tframe);
+
+ if (addr)
+ return addr;
+
+ /* Fallback strategy, will be incorrect for while-stepping frames
+ and multi-location tracepoints. */
+ tpoint = find_next_tracepoint_by_number (NULL, tframe->tpnum);
+ return tpoint->address;
+}
+
+/* Search for the next traceframe whose address is inside or outside
+ the given range. */
+
+static struct traceframe *
+find_next_traceframe_in_range (CORE_ADDR lo, CORE_ADDR hi, int inside_p,
+ int *tfnump)
+{
+ client_state &cs = get_client_state ();
+ struct traceframe *tframe;
+ CORE_ADDR tfaddr;
+
+ *tfnump = cs.current_traceframe + 1;
+ tframe = find_traceframe (*tfnump);
+ /* The search is not supposed to wrap around. */
+ if (!tframe)
+ {
+ *tfnump = -1;
+ return NULL;
+ }
+
+ for (; tframe->tpnum != 0; tframe = NEXT_TRACEFRAME (tframe))
+ {
+ tfaddr = get_traceframe_address (tframe);
+ if (inside_p
+ ? (lo <= tfaddr && tfaddr <= hi)
+ : (lo > tfaddr || tfaddr > hi))
+ return tframe;
+ ++*tfnump;
+ }
+
+ *tfnump = -1;
+ return NULL;
+}
+
+/* Search for the next traceframe recorded by the given tracepoint.
+ Note that for multi-location tracepoints, this will find whatever
+ location appears first. */
+
+static struct traceframe *
+find_next_traceframe_by_tracepoint (int num, int *tfnump)
+{
+ client_state &cs = get_client_state ();
+ struct traceframe *tframe;
+
+ *tfnump = cs.current_traceframe + 1;
+ tframe = find_traceframe (*tfnump);
+ /* The search is not supposed to wrap around. */
+ if (!tframe)
+ {
+ *tfnump = -1;
+ return NULL;
+ }
+
+ for (; tframe->tpnum != 0; tframe = NEXT_TRACEFRAME (tframe))
+ {
+ if (tframe->tpnum == num)
+ return tframe;
+ ++*tfnump;
+ }
+
+ *tfnump = -1;
+ return NULL;
+}
+
+#endif
+
+#ifndef IN_PROCESS_AGENT
+
+/* Clear all past trace state. */
+
+static void
+cmd_qtinit (char *packet)
+{
+ client_state &cs = get_client_state ();
+ struct trace_state_variable *tsv, *prev, *next;
+
+ /* Can't do this command without a pid attached. */
+ if (current_thread == NULL)
+ {
+ write_enn (packet);
+ return;
+ }
+
+ /* Make sure we don't try to read from a trace frame. */
+ cs.current_traceframe = -1;
+
+ stop_tracing ();
+
+ trace_debug ("Initializing the trace");
+
+ clear_installed_tracepoints ();
+ clear_readonly_regions ();
+
+ tracepoints = NULL;
+ last_tracepoint = NULL;
+
+ /* Clear out any leftover trace state variables. Ones with target
+ defined getters should be kept however. */
+ prev = NULL;
+ tsv = trace_state_variables;
+ while (tsv)
+ {
+ trace_debug ("Looking at var %d", tsv->number);
+ if (tsv->getter == NULL)
+ {
+ next = tsv->next;
+ if (prev)
+ prev->next = next;
+ else
+ trace_state_variables = next;
+ trace_debug ("Deleting var %d", tsv->number);
+ free (tsv);
+ tsv = next;
+ }
+ else
+ {
+ prev = tsv;
+ tsv = tsv->next;
+ }
+ }
+
+ clear_trace_buffer ();
+ clear_inferior_trace_buffer ();
+
+ write_ok (packet);
+}
+
+/* Unprobe the UST marker at ADDRESS. */
+
+static void
+unprobe_marker_at (CORE_ADDR address)
+{
+ char cmd[IPA_CMD_BUF_SIZE];
+
+ sprintf (cmd, "unprobe_marker_at:%s", paddress (address));
+ run_inferior_command (cmd, strlen (cmd) + 1);
+}
+
+/* Restore the program to its pre-tracing state. This routine may be called
+ in error situations, so it needs to be careful about only restoring
+ from known-valid bits. */
+
+static void
+clear_installed_tracepoints (void)
+{
+ struct tracepoint *tpoint;
+ struct tracepoint *prev_stpoint;
+
+ pause_all (1);
+
+ prev_stpoint = NULL;
+
+ /* Restore any bytes overwritten by tracepoints. */
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ {
+ /* Catch the case where we might try to remove a tracepoint that
+ was never actually installed. */
+ if (tpoint->handle == NULL)
+ {
+ trace_debug ("Tracepoint %d at 0x%s was "
+ "never installed, nothing to clear",
+ tpoint->number, paddress (tpoint->address));
+ continue;
+ }
+
+ switch (tpoint->type)
+ {
+ case trap_tracepoint:
+ {
+ struct breakpoint *bp
+ = (struct breakpoint *) tpoint->handle;
+
+ delete_breakpoint (bp);
+ }
+ break;
+ case fast_tracepoint:
+ {
+ struct fast_tracepoint_jump *jump
+ = (struct fast_tracepoint_jump *) tpoint->handle;
+
+ delete_fast_tracepoint_jump (jump);
+ }
+ break;
+ case static_tracepoint:
+ if (prev_stpoint != NULL
+ && prev_stpoint->address == tpoint->address)
+ /* Nothing to do. We already unprobed a tracepoint set at
+ this marker address (and there can only be one probe
+ per marker). */
+ ;
+ else
+ {
+ unprobe_marker_at (tpoint->address);
+ prev_stpoint = tpoint;
+ }
+ break;
+ }
+
+ tpoint->handle = NULL;
+ }
+
+ unpause_all (1);
+}
+
+/* Parse a packet that defines a tracepoint. */
+
+static void
+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;
+ struct tracepoint *tpoint;
+ const char *packet = own_buf;
+
+ packet += strlen ("QTDP:");
+
+ /* A hyphen at the beginning marks a packet specifying actions for a
+ tracepoint already supplied. */
+ tppacket = 1;
+ if (*packet == '-')
+ {
+ tppacket = 0;
+ ++packet;
+ }
+ packet = unpack_varlen_hex (packet, &num);
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &addr);
+ ++packet; /* skip a colon */
+
+ /* See if we already have this tracepoint. */
+ tpoint = find_tracepoint (num, addr);
+
+ if (tppacket)
+ {
+ /* Duplicate tracepoints are never allowed. */
+ if (tpoint)
+ {
+ trace_debug ("Tracepoint error: tracepoint %d"
+ " at 0x%s already exists",
+ (int) num, paddress (addr));
+ write_enn (own_buf);
+ return;
+ }
+
+ tpoint = add_tracepoint (num, addr);
+
+ tpoint->enabled = (*packet == 'E');
+ ++packet; /* skip 'E' */
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &count);
+ tpoint->step_count = count;
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &count);
+ tpoint->pass_count = count;
+ /* See if we have any of the additional optional fields. */
+ while (*packet == ':')
+ {
+ ++packet;
+ if (*packet == 'F')
+ {
+ tpoint->type = fast_tracepoint;
+ ++packet;
+ packet = unpack_varlen_hex (packet, &count);
+ tpoint->orig_size = count;
+ }
+ else if (*packet == 'S')
+ {
+ tpoint->type = static_tracepoint;
+ ++packet;
+ }
+ else if (*packet == 'X')
+ {
+ tpoint->cond = gdb_parse_agent_expr (&packet);
+ }
+ else if (*packet == '-')
+ break;
+ else if (*packet == '\0')
+ break;
+ else
+ trace_debug ("Unknown optional tracepoint field");
+ }
+ if (*packet == '-')
+ {
+ trail_hyphen = 1;
+ trace_debug ("Also has actions\n");
+ }
+
+ trace_debug ("Defined %stracepoint %d at 0x%s, "
+ "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);
+ }
+ else if (tpoint)
+ add_tracepoint_action (tpoint, packet);
+ else
+ {
+ trace_debug ("Tracepoint error: tracepoint %d at 0x%s not found",
+ (int) num, paddress (addr));
+ write_enn (own_buf);
+ 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);
+}
+
+static void
+cmd_qtdpsrc (char *own_buf)
+{
+ ULONGEST num, addr, start, slen;
+ struct tracepoint *tpoint;
+ const char *packet = own_buf;
+ const char *saved;
+ char *srctype, *src;
+ size_t nbytes;
+ struct source_string *last, *newlast;
+
+ packet += strlen ("QTDPsrc:");
+
+ packet = unpack_varlen_hex (packet, &num);
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &addr);
+ ++packet; /* skip a colon */
+
+ /* 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;
+ }
+
+ saved = packet;
+ packet = strchr (packet, ':');
+ srctype = (char *) xmalloc (packet - saved + 1);
+ memcpy (srctype, saved, packet - saved);
+ srctype[packet - saved] = '\0';
+ ++packet;
+ packet = unpack_varlen_hex (packet, &start);
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &slen);
+ ++packet; /* skip a colon */
+ src = (char *) xmalloc (slen + 1);
+ nbytes = hex2bin (packet, (gdb_byte *) src, strlen (packet) / 2);
+ src[nbytes] = '\0';
+
+ newlast = XNEW (struct source_string);
+ newlast->type = srctype;
+ newlast->str = src;
+ newlast->next = NULL;
+ /* Always add a source string to the end of the list;
+ this keeps sequences of actions/commands in the right
+ order. */
+ if (tpoint->source_strings)
+ {
+ for (last = tpoint->source_strings; last->next; last = last->next)
+ ;
+ last->next = newlast;
+ }
+ else
+ tpoint->source_strings = newlast;
+
+ write_ok (own_buf);
+}
+
+static void
+cmd_qtdv (char *own_buf)
+{
+ ULONGEST num, val, builtin;
+ char *varname;
+ size_t nbytes;
+ struct trace_state_variable *tsv;
+ const char *packet = own_buf;
+
+ packet += strlen ("QTDV:");
+
+ packet = unpack_varlen_hex (packet, &num);
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &val);
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &builtin);
+ ++packet; /* skip a colon */
+
+ nbytes = strlen (packet) / 2;
+ varname = (char *) xmalloc (nbytes + 1);
+ nbytes = hex2bin (packet, (gdb_byte *) varname, nbytes);
+ varname[nbytes] = '\0';
+
+ tsv = create_trace_state_variable (num, 1);
+ tsv->initial_value = (LONGEST) val;
+ tsv->name = varname;
+
+ set_trace_state_variable_value (num, (LONGEST) val);
+
+ write_ok (own_buf);
+}
+
+static void
+cmd_qtenable_disable (char *own_buf, int enable)
+{
+ const 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_int8 (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)
+{
+ client_state &cs = get_client_state ();
+ ULONGEST num;
+ LONGEST val = 0;
+ int err;
+ char *packet = own_buf;
+
+ packet += strlen ("qTV:");
+ unpack_varlen_hex (packet, &num);
+
+ if (cs.current_traceframe >= 0)
+ {
+ err = traceframe_read_tsv ((int) num, &val);
+ if (err)
+ {
+ strcpy (own_buf, "U");
+ return;
+ }
+ }
+ /* Only make tsv's be undefined before the first trace run. After a
+ trace run is over, the user might want to see the last value of
+ the tsv, and it might not be available in a traceframe. */
+ else if (!tracing && strcmp (tracing_stop_reason, "tnotrun") == 0)
+ {
+ strcpy (own_buf, "U");
+ return;
+ }
+ else
+ val = get_trace_state_variable_value (num);
+
+ sprintf (own_buf, "V%s", phex_nz (val, 0));
+}
+
+/* Clear out the list of readonly regions. */
+
+static void
+clear_readonly_regions (void)
+{
+ struct readonly_region *roreg;
+
+ while (readonly_regions)
+ {
+ roreg = readonly_regions;
+ readonly_regions = readonly_regions->next;
+ free (roreg);
+ }
+}
+
+/* Parse the collection of address ranges whose contents GDB believes
+ to be unchanging and so can be read directly from target memory
+ even while looking at a traceframe. */
+
+static void
+cmd_qtro (char *own_buf)
+{
+ ULONGEST start, end;
+ struct readonly_region *roreg;
+ const char *packet = own_buf;
+
+ trace_debug ("Want to mark readonly regions");
+
+ clear_readonly_regions ();
+
+ packet += strlen ("QTro");
+
+ while (*packet == ':')
+ {
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &start);
+ ++packet; /* skip a comma */
+ packet = unpack_varlen_hex (packet, &end);
+
+ roreg = XNEW (struct readonly_region);
+ roreg->start = start;
+ roreg->end = end;
+ roreg->next = readonly_regions;
+ readonly_regions = roreg;
+ trace_debug ("Added readonly region from 0x%s to 0x%s",
+ paddress (roreg->start), paddress (roreg->end));
+ }
+
+ write_ok (own_buf);
+}
+
+/* Test to see if the given range is in our list of readonly ranges.
+ We only test for being entirely within a range, GDB is not going to
+ send a single memory packet that spans multiple regions. */
+
+int
+in_readonly_region (CORE_ADDR addr, ULONGEST length)
+{
+ struct readonly_region *roreg;
+
+ for (roreg = readonly_regions; roreg; roreg = roreg->next)
+ if (roreg->start <= addr && (addr + length - 1) <= roreg->end)
+ return 1;
+
+ return 0;
+}
+
+static CORE_ADDR gdb_jump_pad_head;
+
+/* Return the address of the next free jump space. */
+
+static CORE_ADDR
+get_jump_space_head (void)
+{
+ if (gdb_jump_pad_head == 0)
+ {
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_jump_pad_buffer,
+ &gdb_jump_pad_head))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting jump_pad_buffer");
+ }
+ }
+
+ return gdb_jump_pad_head;
+}
+
+/* Reserve USED bytes from the jump space. */
+
+static void
+claim_jump_space (ULONGEST used)
+{
+ trace_debug ("claim_jump_space reserves %s bytes at %s",
+ pulongest (used), paddress (gdb_jump_pad_head));
+ gdb_jump_pad_head += used;
+}
+
+static CORE_ADDR trampoline_buffer_head = 0;
+static CORE_ADDR trampoline_buffer_tail;
+
+/* 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. */
+
+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))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting trampoline_buffer");
+ }
+
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_end,
+ &trampoline_buffer_head))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting trampoline_buffer_end");
+ }
+ }
+
+ /* 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;
+ }
+
+ trampoline_buffer_head -= used;
+
+ trace_debug ("claim_trampoline_space reserves %s bytes at %s",
+ pulongest (used), paddress (trampoline_buffer_head));
+
+ *trampoline = trampoline_buffer_head;
+ return 1;
+}
+
+/* Returns non-zero if there is space allocated for use in trampolines
+ for fast tracepoints. */
+
+int
+have_fast_tracepoint_trampoline_buffer (char *buf)
+{
+ CORE_ADDR trampoline_end, errbuf;
+
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_end,
+ &trampoline_end))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting trampoline_buffer_end");
+ }
+
+ if (buf)
+ {
+ buf[0] = '\0';
+ strcpy (buf, "was claiming");
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_error,
+ &errbuf))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting errbuf");
+ }
+
+ 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, strlen (cmd) + 1);
+
+ if (err == 0)
+ {
+ if (*cmd == 'E')
+ {
+ strcpy (errout, cmd);
+ return -1;
+ }
+ }
+
+ return err;
+}
+
+static void
+clone_fast_tracepoint (struct tracepoint *to, const struct tracepoint *from)
+{
+ 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;
+ CORE_ADDR collect;
+ 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;
+
+ 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;
+ }
+
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_collect_ptr,
+ &collect))
+ {
+ error ("error extracting gdb_collect_ptr");
+ return 1;
+ }
+
+ 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,
+ 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);
+
+static void
+cmd_qtstart (char *packet)
+{
+ struct tracepoint *tpoint, *prev_ftpoint, *prev_stpoint;
+ CORE_ADDR tpptr = 0, prev_tpptr = 0;
+
+ trace_debug ("Starting the trace");
+
+ /* Pause all threads temporarily while we patch tracepoints. */
+ pause_all (0);
+
+ /* Get threads out of jump pads. Safe to do here, since this is a
+ top level command. And, required to do here, since we're
+ deleting/rewriting jump pads. */
+
+ stabilize_threads ();
+
+ /* Freeze threads. */
+ pause_all (1);
+
+ /* Sync the fast tracepoints list in the inferior ftlib. */
+ if (agent_loaded_p ())
+ download_trace_state_variables ();
+
+ /* No previous fast tpoint yet. */
+ prev_ftpoint = NULL;
+
+ /* No previous static tpoint yet. */
+ prev_stpoint = NULL;
+
+ *packet = '\0';
+
+ if (agent_loaded_p ())
+ {
+ /* Tell IPA about the correct tdesc. */
+ if (write_inferior_integer (ipa_sym_addrs.addr_ipa_tdesc_idx,
+ target_get_ipa_tdesc_idx ()))
+ error ("Error setting ipa_tdesc_idx variable in lib");
+ }
+
+ /* Start out empty. */
+ if (agent_loaded_p ())
+ write_inferior_data_pointer (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;
+ tpoint->traceframe_usage = 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 (maybe_write_ipa_not_loaded (packet))
+ {
+ trace_debug ("Requested a %s tracepoint, but fast "
+ "tracepoints aren't supported.",
+ tpoint->type == static_tracepoint
+ ? "static" : "fast");
+ break;
+ }
+
+ if (tpoint->type == fast_tracepoint)
+ {
+ int use_agent_p
+ = use_agent && agent_capability_check (AGENT_CAPA_FAST_TRACE);
+
+ if (prev_ftpoint != NULL
+ && prev_ftpoint->address == tpoint->address)
+ {
+ if (use_agent_p)
+ tracepoint_send_agent (tpoint);
+ else
+ download_tracepoint_1 (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 (!in_process_agent_supports_ust ())
+ {
+ trace_debug ("Requested a static tracepoint, but static "
+ "tracepoints are not supported.");
+ break;
+ }
+
+ 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_pointer (ipa_sym_addrs.addr_tracepoints, tpptr);
+ else
+ write_inferior_data_pointer (prev_tpptr
+ + offsetof (struct tracepoint, next),
+ tpptr);
+ }
+
+ /* Any failure in the inner loop is sufficient cause to give
+ up. */
+ if (tpoint->handle == NULL)
+ break;
+ }
+
+ /* Any error in tracepoint insertion is unacceptable; better to
+ address the problem now, than end up with a useless or misleading
+ trace run. */
+ if (tpoint != NULL)
+ {
+ clear_installed_tracepoints ();
+ if (*packet == '\0')
+ write_enn (packet);
+ unpause_all (1);
+ return;
+ }
+
+ stopping_tracepoint = NULL;
+ 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 (agent_loaded_p ())
+ {
+ if (write_inferior_integer (ipa_sym_addrs.addr_tracing, 1))
+ {
+ internal_error (__FILE__, __LINE__,
+ "Error setting tracing variable in lib");
+ }
+
+ if (write_inferior_data_pointer (ipa_sym_addrs.addr_stopping_tracepoint,
+ 0))
+ {
+ internal_error (__FILE__, __LINE__,
+ "Error clearing stopping_tracepoint variable"
+ " in lib");
+ }
+
+ if (write_inferior_integer (ipa_sym_addrs.addr_trace_buffer_is_full, 0))
+ {
+ internal_error (__FILE__, __LINE__,
+ "Error clearing trace_buffer_is_full variable"
+ " in lib");
+ }
+
+ stop_tracing_bkpt = set_breakpoint_at (ipa_sym_addrs.addr_stop_tracing,
+ stop_tracing_handler);
+ if (stop_tracing_bkpt == NULL)
+ error ("Error setting stop_tracing breakpoint");
+
+ flush_trace_buffer_bkpt
+ = set_breakpoint_at (ipa_sym_addrs.addr_flush_trace_buffer,
+ flush_trace_buffer_handler);
+ if (flush_trace_buffer_bkpt == NULL)
+ error ("Error setting flush_trace_buffer breakpoint");
+ }
+
+ unpause_all (1);
+
+ write_ok (packet);
+}
+
+/* End a tracing run, filling in a stop reason to report back to GDB,
+ and removing the tracepoints from the code. */
+
+void
+stop_tracing (void)
+{
+ if (!tracing)
+ {
+ trace_debug ("Tracing is already off, ignoring");
+ return;
+ }
+
+ trace_debug ("Stopping the trace");
+
+ /* Pause all threads before removing fast jumps from memory,
+ breakpoints, and touching IPA state variables (inferior memory).
+ Some thread may hit the internal tracing breakpoints, or be
+ collecting this moment, but that's ok, we don't release the
+ tpoint object's memory or the jump pads here (we only do that
+ when we're sure we can move all threads out of the jump pads).
+ We can't now, since we may be getting here due to the inferior
+ agent calling us. */
+ pause_all (1);
+
+ /* Stop logging. Tracepoints can still be hit, but they will not be
+ recorded. */
+ tracing = 0;
+ if (agent_loaded_p ())
+ {
+ if (write_inferior_integer (ipa_sym_addrs.addr_tracing, 0))
+ {
+ internal_error (__FILE__, __LINE__,
+ "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 %" PRIu64 " times",
+ stopping_tracepoint->number,
+ stopping_tracepoint->pass_count);
+ tracing_stop_reason = "tpasscount";
+ tracing_stop_tpnum = stopping_tracepoint->number;
+ }
+ else if (trace_buffer_is_full)
+ {
+ trace_debug ("Stopping the trace because the trace buffer is full");
+ tracing_stop_reason = "tfull";
+ }
+ else if (expr_eval_result != expr_eval_no_error)
+ {
+ trace_debug ("Stopping the trace because of an expression eval error");
+ tracing_stop_reason = eval_result_names[expr_eval_result];
+ tracing_stop_tpnum = error_tracepoint->number;
+ }
+#ifndef IN_PROCESS_AGENT
+ else if (!gdb_connected ())
+ {
+ trace_debug ("Stopping the trace because GDB disconnected");
+ tracing_stop_reason = "tdisconnected";
+ }
+#endif
+ else
+ {
+ trace_debug ("Stopping the trace because of a tstop command");
+ tracing_stop_reason = "tstop";
+ }
+
+ stopping_tracepoint = NULL;
+ error_tracepoint = NULL;
+
+ /* Clear out the tracepoints. */
+ clear_installed_tracepoints ();
+
+ 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,
+ because we want to present the full number of created frames
+ in addition to what fit in the trace buffer. */
+ upload_fast_traceframes ();
+ }
+
+ if (stop_tracing_bkpt != NULL)
+ {
+ delete_breakpoint (stop_tracing_bkpt);
+ stop_tracing_bkpt = NULL;
+ }
+
+ if (flush_trace_buffer_bkpt != NULL)
+ {
+ delete_breakpoint (flush_trace_buffer_bkpt);
+ flush_trace_buffer_bkpt = NULL;
+ }
+
+ unpause_all (1);
+}
+
+static int
+stop_tracing_handler (CORE_ADDR addr)
+{
+ trace_debug ("lib hit stop_tracing");
+
+ /* Don't actually handle it here. When we stop tracing we remove
+ breakpoints from the inferior, and that is not allowed in a
+ breakpoint handler (as the caller is walking the breakpoint
+ list). */
+ return 0;
+}
+
+static int
+flush_trace_buffer_handler (CORE_ADDR addr)
+{
+ trace_debug ("lib hit flush_trace_buffer");
+ return 0;
+}
+
+static void
+cmd_qtstop (char *packet)
+{
+ stop_tracing ();
+ write_ok (packet);
+}
+
+static void
+cmd_qtdisconnected (char *own_buf)
+{
+ ULONGEST setting;
+ char *packet = own_buf;
+
+ packet += strlen ("QTDisconnected:");
+
+ unpack_varlen_hex (packet, &setting);
+
+ write_ok (own_buf);
+
+ disconnected_tracing = setting;
+}
+
+static void
+cmd_qtframe (char *own_buf)
+{
+ client_state &cs = get_client_state ();
+ ULONGEST frame, pc, lo, hi, num;
+ int tfnum, tpnum;
+ struct traceframe *tframe;
+ const char *packet = own_buf;
+
+ packet += strlen ("QTFrame:");
+
+ if (startswith (packet, "pc:"))
+ {
+ packet += strlen ("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);
+ }
+ else if (startswith (packet, "range:"))
+ {
+ packet += strlen ("range:");
+ packet = unpack_varlen_hex (packet, &lo);
+ ++packet;
+ 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);
+ }
+ else if (startswith (packet, "outside:"))
+ {
+ packet += strlen ("outside:");
+ packet = unpack_varlen_hex (packet, &lo);
+ ++packet;
+ unpack_varlen_hex (packet, &hi);
+ trace_debug ("Want to find next traceframe "
+ "outside the range 0x%s to 0x%s",
+ paddress (lo), paddress (hi));
+ tframe = find_next_traceframe_in_range (lo, hi, 0, &tfnum);
+ }
+ else if (startswith (packet, "tdp:"))
+ {
+ packet += strlen ("tdp:");
+ 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);
+ }
+ else
+ {
+ unpack_varlen_hex (packet, &frame);
+ tfnum = (int) frame;
+ if (tfnum == -1)
+ {
+ trace_debug ("Want to stop looking at traceframes");
+ cs.current_traceframe = -1;
+ write_ok (own_buf);
+ return;
+ }
+ trace_debug ("Want to look at traceframe %d", tfnum);
+ tframe = find_traceframe (tfnum);
+ }
+
+ if (tframe)
+ {
+ cs.current_traceframe = tfnum;
+ sprintf (own_buf, "F%xT%x", tfnum, tframe->tpnum);
+ }
+ else
+ sprintf (own_buf, "F-1");
+}
+
+static void
+cmd_qtstatus (char *packet)
+{
+ char *stop_reason_rsp = NULL;
+ char *buf1, *buf2, *buf3;
+ const char *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);
+ bin2hex ((gdb_byte *) str, buf1, slen);
+
+ str = (tracing_notes ? tracing_notes : "");
+ slen = strlen (str);
+ buf2 = (char *) alloca (slen * 2 + 1);
+ bin2hex ((gdb_byte *) str, buf2, slen);
+
+ str = (tracing_stop_note ? tracing_stop_note : "");
+ slen = strlen (str);
+ buf3 = (char *) alloca (slen * 2 + 1);
+ bin2hex ((gdb_byte *) str, buf3, slen);
+
+ trace_debug ("Returning trace status as %d, stop reason %s",
+ tracing, tracing_stop_reason);
+
+ if (agent_loaded_p ())
+ {
+ pause_all (1);
+
+ upload_fast_traceframes ();
+
+ unpause_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 `tracing_stop_reason' to ease
+ debugging. */
+ if (startswith (stop_reason_rsp, "terror:"))
+ {
+ const char *result_name;
+ int hexstr_len;
+ char *p;
+
+ result_name = stop_reason_rsp + strlen ("terror:");
+ hexstr_len = strlen (result_name) * 2;
+ p = stop_reason_rsp
+ = (char *) alloca (strlen ("terror:") + hexstr_len + 1);
+ strcpy (p, "terror:");
+ p += strlen (p);
+ bin2hex ((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 = (char *) 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;"
+ "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,
+ phex_nz (tracing_start_time, sizeof (tracing_start_time)),
+ phex_nz (tracing_stop_time, sizeof (tracing_stop_time)),
+ buf1, buf2);
+}
+
+static void
+cmd_qtp (char *own_buf)
+{
+ ULONGEST num, addr;
+ struct tracepoint *tpoint;
+ const 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. */
+static struct tracepoint *cur_tpoint;
+static unsigned int cur_action;
+static unsigned int cur_step_action;
+static struct source_string *cur_source_string;
+static struct trace_state_variable *cur_tsv;
+
+/* Compose a response that is an imitation of the syntax by which the
+ tracepoint was originally downloaded. */
+
+static void
+response_tracepoint (char *packet, struct tracepoint *tpoint)
+{
+ char *buf;
+
+ 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->type == fast_tracepoint)
+ sprintf (packet + strlen (packet), ":F%x", tpoint->orig_size);
+ else if (tpoint->type == static_tracepoint)
+ sprintf (packet + strlen (packet), ":S");
+
+ if (tpoint->cond)
+ {
+ buf = gdb_unparse_agent_expr (tpoint->cond);
+ sprintf (packet + strlen (packet), ":X%x,%s",
+ tpoint->cond->length, buf);
+ free (buf);
+ }
+}
+
+/* Compose a response that is an imitation of the syntax by which the
+ tracepoint action was originally downloaded (with the difference
+ that due to the way we store the actions, this will output a packet
+ per action, while GDB could have combined more than one action
+ per-packet. */
+
+static void
+response_action (char *packet, struct tracepoint *tpoint,
+ char *taction, int step)
+{
+ sprintf (packet, "%c%x:%s:%s",
+ (step ? 'S' : 'A'), tpoint->number, paddress (tpoint->address),
+ taction);
+}
+
+/* Compose a response that is an imitation of the syntax by which the
+ tracepoint source piece was originally downloaded. */
+
+static void
+response_source (char *packet,
+ struct tracepoint *tpoint, struct source_string *src)
+{
+ char *buf;
+ int len;
+
+ len = strlen (src->str);
+ buf = (char *) alloca (len * 2 + 1);
+ bin2hex ((gdb_byte *) src->str, buf, len);
+
+ sprintf (packet, "Z%x:%s:%s:%x:%x:%s",
+ tpoint->number, paddress (tpoint->address),
+ src->type, 0, len, buf);
+}
+
+/* Return the first piece of tracepoint definition, and initialize the
+ state machine that will iterate through all the tracepoint
+ bits. */
+
+static void
+cmd_qtfp (char *packet)
+{
+ trace_debug ("Returning first tracepoint definition piece");
+
+ cur_tpoint = tracepoints;
+ cur_action = cur_step_action = 0;
+ cur_source_string = NULL;
+
+ if (cur_tpoint)
+ response_tracepoint (packet, cur_tpoint);
+ else
+ strcpy (packet, "l");
+}
+
+/* Return additional pieces of tracepoint definition. Each action and
+ stepping action must go into its own packet, because of packet size
+ limits, and so we use state variables to deliver one piece at a
+ time. */
+
+static void
+cmd_qtsp (char *packet)
+{
+ trace_debug ("Returning subsequent tracepoint definition piece");
+
+ if (!cur_tpoint)
+ {
+ /* This case would normally never occur, but be prepared for
+ GDB misbehavior. */
+ strcpy (packet, "l");
+ }
+ else if (cur_action < cur_tpoint->numactions)
+ {
+ response_action (packet, cur_tpoint,
+ cur_tpoint->actions_str[cur_action], 0);
+ ++cur_action;
+ }
+ else if (cur_step_action < cur_tpoint->num_step_actions)
+ {
+ response_action (packet, cur_tpoint,
+ cur_tpoint->step_actions_str[cur_step_action], 1);
+ ++cur_step_action;
+ }
+ else if ((cur_source_string
+ ? cur_source_string->next
+ : cur_tpoint->source_strings))
+ {
+ if (cur_source_string)
+ cur_source_string = cur_source_string->next;
+ else
+ cur_source_string = cur_tpoint->source_strings;
+ response_source (packet, cur_tpoint, cur_source_string);
+ }
+ else
+ {
+ cur_tpoint = cur_tpoint->next;
+ cur_action = cur_step_action = 0;
+ cur_source_string = NULL;
+ if (cur_tpoint)
+ response_tracepoint (packet, cur_tpoint);
+ else
+ strcpy (packet, "l");
+ }
+}
+
+/* Compose a response that is an imitation of the syntax by which the
+ trace state variable was originally downloaded. */
+
+static void
+response_tsv (char *packet, struct trace_state_variable *tsv)
+{
+ char *buf = (char *) "";
+ int namelen;
+
+ if (tsv->name)
+ {
+ namelen = strlen (tsv->name);
+ buf = (char *) alloca (namelen * 2 + 1);
+ bin2hex ((gdb_byte *) tsv->name, buf, namelen);
+ }
+
+ sprintf (packet, "%x:%s:%x:%s", tsv->number, phex_nz (tsv->initial_value, 0),
+ tsv->getter ? 1 : 0, buf);
+}
+
+/* Return the first trace state variable definition, and initialize
+ the state machine that will iterate through all the tsv bits. */
+
+static void
+cmd_qtfv (char *packet)
+{
+ trace_debug ("Returning first trace state variable definition");
+
+ cur_tsv = trace_state_variables;
+
+ if (cur_tsv)
+ response_tsv (packet, cur_tsv);
+ else
+ strcpy (packet, "l");
+}
+
+/* Return additional trace state variable definitions. */
+
+static void
+cmd_qtsv (char *packet)
+{
+ trace_debug ("Returning additional trace state variable definition");
+
+ if (cur_tsv)
+ {
+ cur_tsv = cur_tsv->next;
+ if (cur_tsv)
+ response_tsv (packet, cur_tsv);
+ else
+ strcpy (packet, "l");
+ }
+ else
+ strcpy (packet, "l");
+}
+
+/* Return the first static tracepoint marker, and initialize the state
+ machine that will iterate through all the static tracepoints
+ markers. */
+
+static void
+cmd_qtfstm (char *packet)
+{
+ if (!maybe_write_ipa_ust_not_loaded (packet))
+ run_inferior_command (packet, strlen (packet) + 1);
+}
+
+/* Return additional static tracepoints markers. */
+
+static void
+cmd_qtsstm (char *packet)
+{
+ if (!maybe_write_ipa_ust_not_loaded (packet))
+ run_inferior_command (packet, strlen (packet) + 1);
+}
+
+/* Return the definition of the static tracepoint at a given address.
+ Result packet is the same as qTsST's. */
+
+static void
+cmd_qtstmat (char *packet)
+{
+ if (!maybe_write_ipa_ust_not_loaded (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 *saved_thread;
+
+ saved_thread = current_thread;
+
+ /* Find any thread which belongs to process PID. */
+ current_thread = find_any_thread_of_pid (pid);
+
+ strcpy (buf, "close");
+
+ run_inferior_command (buf, strlen (buf) + 1);
+
+ current_thread = saved_thread;
+ }
+}
+
+/* Return the minimum instruction size needed for fast tracepoints as a
+ hexadecimal number. */
+
+static void
+cmd_qtminftpilen (char *packet)
+{
+ if (current_thread == 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
+ buffer. GDB may ask for a lot, but we are allowed to reply with
+ only as much as will fit within packet limits or whatever. */
+
+static void
+cmd_qtbuffer (char *own_buf)
+{
+ ULONGEST offset, num, tot;
+ unsigned char *tbp;
+ const char *packet = own_buf;
+
+ packet += strlen ("qTBuffer:");
+
+ packet = unpack_varlen_hex (packet, &offset);
+ ++packet; /* skip a comma */
+ unpack_varlen_hex (packet, &num);
+
+ trace_debug ("Want to get trace buffer, %d bytes at offset 0x%s",
+ (int) num, phex_nz (offset, 0));
+
+ tot = (trace_buffer_hi - trace_buffer_lo) - free_space ();
+
+ /* If we're right at the end, reply specially that we're done. */
+ if (offset == tot)
+ {
+ strcpy (own_buf, "l");
+ return;
+ }
+
+ /* Object to any other out-of-bounds request. */
+ if (offset > tot)
+ {
+ write_enn (own_buf);
+ return;
+ }
+
+ /* Compute the pointer corresponding to the given offset, accounting
+ for wraparound. */
+ tbp = trace_buffer_start + offset;
+ if (tbp >= trace_buffer_wrap)
+ tbp -= (trace_buffer_wrap - trace_buffer_lo);
+
+ /* Trim to the remaining bytes if we're close to the end. */
+ if (num > tot - offset)
+ num = tot - offset;
+
+ /* Trim to available packet size. */
+ if (num >= (PBUFSIZ - 16) / 2 )
+ num = (PBUFSIZ - 16) / 2;
+
+ bin2hex (tbp, own_buf, num);
+}
+
+static void
+cmd_bigqtbuffer_circular (char *own_buf)
+{
+ ULONGEST val;
+ char *packet = own_buf;
+
+ 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_bigqtbuffer_size (char *own_buf)
+{
+ ULONGEST val;
+ LONGEST sval;
+ char *packet = own_buf;
+
+ /* Can't change the size during a tracing run. */
+ if (tracing)
+ {
+ write_enn (own_buf);
+ return;
+ }
+
+ packet += strlen ("QTBuffer:size:");
+
+ /* -1 is sent as literal "-1". */
+ if (strcmp (packet, "-1") == 0)
+ sval = DEFAULT_TRACE_BUFFER_SIZE;
+ else
+ {
+ unpack_varlen_hex (packet, &val);
+ sval = (LONGEST) val;
+ }
+
+ init_trace_buffer (sval);
+ trace_debug ("Trace buffer is now %s bytes",
+ plongest (trace_buffer_size));
+ 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:");
+
+ while (*packet)
+ {
+ if (startswith (packet, "user:"))
+ {
+ packet += strlen ("user:");
+ saved = packet;
+ packet = strchr (packet, ';');
+ nbytes = (packet - saved) / 2;
+ user = (char *) xmalloc (nbytes + 1);
+ nbytes = hex2bin (saved, (gdb_byte *) user, nbytes);
+ user[nbytes] = '\0';
+ ++packet; /* skip the semicolon */
+ trace_debug ("User is '%s'", user);
+ xfree (tracing_user_name);
+ tracing_user_name = user;
+ }
+ else if (startswith (packet, "notes:"))
+ {
+ packet += strlen ("notes:");
+ saved = packet;
+ packet = strchr (packet, ';');
+ nbytes = (packet - saved) / 2;
+ notes = (char *) xmalloc (nbytes + 1);
+ nbytes = hex2bin (saved, (gdb_byte *) notes, nbytes);
+ notes[nbytes] = '\0';
+ ++packet; /* skip the semicolon */
+ trace_debug ("Notes is '%s'", notes);
+ xfree (tracing_notes);
+ tracing_notes = notes;
+ }
+ else if (startswith (packet, "tstop:"))
+ {
+ packet += strlen ("tstop:");
+ saved = packet;
+ packet = strchr (packet, ';');
+ nbytes = (packet - saved) / 2;
+ stopnote = (char *) xmalloc (nbytes + 1);
+ nbytes = hex2bin (saved, (gdb_byte *) stopnote, 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;
+ }
+
+ write_ok (own_buf);
+}
+
+int
+handle_tracepoint_general_set (char *packet)
+{
+ if (strcmp ("QTinit", packet) == 0)
+ {
+ cmd_qtinit (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTDP:"))
+ {
+ cmd_qtdp (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTDPsrc:"))
+ {
+ cmd_qtdpsrc (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTEnable:"))
+ {
+ cmd_qtenable_disable (packet, 1);
+ return 1;
+ }
+ else if (startswith (packet, "QTDisable:"))
+ {
+ cmd_qtenable_disable (packet, 0);
+ return 1;
+ }
+ else if (startswith (packet, "QTDV:"))
+ {
+ cmd_qtdv (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTro:"))
+ {
+ cmd_qtro (packet);
+ return 1;
+ }
+ else if (strcmp ("QTStart", packet) == 0)
+ {
+ cmd_qtstart (packet);
+ return 1;
+ }
+ else if (strcmp ("QTStop", packet) == 0)
+ {
+ cmd_qtstop (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTDisconnected:"))
+ {
+ cmd_qtdisconnected (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTFrame:"))
+ {
+ cmd_qtframe (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTBuffer:circular:"))
+ {
+ cmd_bigqtbuffer_circular (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTBuffer:size:"))
+ {
+ cmd_bigqtbuffer_size (packet);
+ return 1;
+ }
+ else if (startswith (packet, "QTNotes:"))
+ {
+ cmd_qtnotes (packet);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+handle_tracepoint_query (char *packet)
+{
+ if (strcmp ("qTStatus", packet) == 0)
+ {
+ cmd_qtstatus (packet);
+ return 1;
+ }
+ else if (startswith (packet, "qTP:"))
+ {
+ cmd_qtp (packet);
+ return 1;
+ }
+ else if (strcmp ("qTfP", packet) == 0)
+ {
+ cmd_qtfp (packet);
+ return 1;
+ }
+ else if (strcmp ("qTsP", packet) == 0)
+ {
+ cmd_qtsp (packet);
+ return 1;
+ }
+ else if (strcmp ("qTfV", packet) == 0)
+ {
+ cmd_qtfv (packet);
+ return 1;
+ }
+ else if (strcmp ("qTsV", packet) == 0)
+ {
+ cmd_qtsv (packet);
+ return 1;
+ }
+ else if (startswith (packet, "qTV:"))
+ {
+ cmd_qtv (packet);
+ return 1;
+ }
+ else if (startswith (packet, "qTBuffer:"))
+ {
+ cmd_qtbuffer (packet);
+ return 1;
+ }
+ else if (strcmp ("qTfSTM", packet) == 0)
+ {
+ cmd_qtfstm (packet);
+ return 1;
+ }
+ else if (strcmp ("qTsSTM", packet) == 0)
+ {
+ cmd_qtsstm (packet);
+ return 1;
+ }
+ else if (startswith (packet, "qTSTMat:"))
+ {
+ cmd_qtstmat (packet);
+ return 1;
+ }
+ else if (strcmp ("qTMinFTPILen", packet) == 0)
+ {
+ cmd_qtminftpilen (packet);
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
+#ifndef IN_PROCESS_AGENT
+
+/* Call this when thread TINFO has hit the tracepoint defined by
+ TP_NUMBER and TP_ADDRESS, and that tracepoint has a while-stepping
+ action. This adds a while-stepping collecting state item to the
+ threads' collecting state list, so that we can keep track of
+ multiple simultaneous while-stepping actions being collected by the
+ same thread. This can happen in cases like:
+
+ ff0001 INSN1 <-- TP1, while-stepping 10 collect $regs
+ ff0002 INSN2
+ ff0003 INSN3 <-- TP2, collect $regs
+ ff0004 INSN4 <-- TP3, while-stepping 10 collect $regs
+ ff0005 INSN5
+
+ Notice that when instruction INSN5 is reached, the while-stepping
+ actions of both TP1 and TP3 are still being collected, and that TP2
+ had been collected meanwhile. The whole range of ff0001-ff0005
+ should be single-stepped, due to at least TP1's while-stepping
+ action covering the whole range. */
+
+static void
+add_while_stepping_state (struct thread_info *tinfo,
+ int tp_number, CORE_ADDR tp_address)
+{
+ struct wstep_state *wstep = XNEW (struct wstep_state);
+
+ wstep->next = tinfo->while_stepping;
+
+ wstep->tp_number = tp_number;
+ wstep->tp_address = tp_address;
+ wstep->current_step = 0;
+
+ tinfo->while_stepping = wstep;
+}
+
+/* Release the while-stepping collecting state WSTEP. */
+
+static void
+release_while_stepping_state (struct wstep_state *wstep)
+{
+ free (wstep);
+}
+
+/* Release all while-stepping collecting states currently associated
+ with thread TINFO. */
+
+void
+release_while_stepping_state_list (struct thread_info *tinfo)
+{
+ struct wstep_state *head;
+
+ while (tinfo->while_stepping)
+ {
+ head = tinfo->while_stepping;
+ tinfo->while_stepping = head->next;
+ release_while_stepping_state (head);
+ }
+}
+
+/* If TINFO was handling a 'while-stepping' action, the step has
+ finished, so collect any step data needed, and check if any more
+ steps are required. Return true if the thread was indeed
+ collecting tracepoint data, false otherwise. */
+
+int
+tracepoint_finished_step (struct thread_info *tinfo, CORE_ADDR stop_pc)
+{
+ struct tracepoint *tpoint;
+ struct wstep_state *wstep;
+ struct wstep_state **wstep_link;
+ struct trap_tracepoint_ctx ctx;
+
+ /* Pull in fast tracepoint trace frames from the inferior lib buffer into
+ our buffer. */
+ if (agent_loaded_p ())
+ upload_fast_traceframes ();
+
+ /* Check if we were indeed collecting data for one of more
+ tracepoints with a 'while-stepping' count. */
+ if (tinfo->while_stepping == NULL)
+ return 0;
+
+ if (!tracing)
+ {
+ /* We're not even tracing anymore. Stop this thread from
+ collecting. */
+ release_while_stepping_state_list (tinfo);
+
+ /* The thread had stopped due to a single-step request indeed
+ explained by a tracepoint. */
+ return 1;
+ }
+
+ wstep = tinfo->while_stepping;
+ wstep_link = &tinfo->while_stepping;
+
+ trace_debug ("Thread %s finished a single-step for tracepoint %d at 0x%s",
+ target_pid_to_str (tinfo->id),
+ wstep->tp_number, paddress (wstep->tp_address));
+
+ ctx.base.type = trap_tracepoint;
+ ctx.regcache = get_thread_regcache (tinfo, 1);
+
+ while (wstep != NULL)
+ {
+ tpoint = find_tracepoint (wstep->tp_number, wstep->tp_address);
+ if (tpoint == NULL)
+ {
+ trace_debug ("NO TRACEPOINT %d at 0x%s FOR THREAD %s!",
+ wstep->tp_number, paddress (wstep->tp_address),
+ target_pid_to_str (tinfo->id));
+
+ /* Unlink. */
+ *wstep_link = wstep->next;
+ release_while_stepping_state (wstep);
+ wstep = *wstep_link;
+ continue;
+ }
+
+ /* We've just finished one step. */
+ ++wstep->current_step;
+
+ /* Collect data. */
+ collect_data_at_step ((struct tracepoint_hit_ctx *) &ctx,
+ stop_pc, tpoint, wstep->current_step);
+
+ if (wstep->current_step >= tpoint->step_count)
+ {
+ /* The requested numbers of steps have occurred. */
+ trace_debug ("Thread %s done stepping for tracepoint %d at 0x%s",
+ target_pid_to_str (tinfo->id),
+ wstep->tp_number, paddress (wstep->tp_address));
+
+ /* Unlink the wstep. */
+ *wstep_link = wstep->next;
+ release_while_stepping_state (wstep);
+ wstep = *wstep_link;
+
+ /* Only check the hit count now, which ensure that we do all
+ our stepping before stopping the run. */
+ if (tpoint->pass_count > 0
+ && tpoint->hit_count >= tpoint->pass_count
+ && stopping_tracepoint == NULL)
+ stopping_tracepoint = tpoint;
+ }
+ else
+ {
+ /* Keep single-stepping until the requested numbers of steps
+ have occurred. */
+ wstep_link = &wstep->next;
+ wstep = *wstep_link;
+ }
+
+ if (stopping_tracepoint
+ || trace_buffer_is_full
+ || expr_eval_result != expr_eval_no_error)
+ {
+ stop_tracing ();
+ break;
+ }
+ }
+
+ return 1;
+}
+
+/* Handle any internal tracing control breakpoint hits. That means,
+ pull traceframes from the IPA to our buffer, and syncing both
+ tracing agents when the IPA's tracing stops for some reason. */
+
+int
+handle_tracepoint_bkpts (struct thread_info *tinfo, CORE_ADDR stop_pc)
+{
+ /* Pull in fast tracepoint trace frames from the inferior in-process
+ agent's buffer into our buffer. */
+
+ if (!agent_loaded_p ())
+ return 0;
+
+ upload_fast_traceframes ();
+
+ /* Check if the in-process agent had decided we should stop
+ tracing. */
+ if (stop_pc == ipa_sym_addrs.addr_stop_tracing)
+ {
+ int ipa_trace_buffer_is_full;
+ CORE_ADDR ipa_stopping_tracepoint;
+ int ipa_expr_eval_result;
+ CORE_ADDR ipa_error_tracepoint;
+
+ trace_debug ("lib stopped at stop_tracing");
+
+ read_inferior_integer (ipa_sym_addrs.addr_trace_buffer_is_full,
+ &ipa_trace_buffer_is_full);
+
+ read_inferior_data_pointer (ipa_sym_addrs.addr_stopping_tracepoint,
+ &ipa_stopping_tracepoint);
+ write_inferior_data_pointer (ipa_sym_addrs.addr_stopping_tracepoint, 0);
+
+ read_inferior_data_pointer (ipa_sym_addrs.addr_error_tracepoint,
+ &ipa_error_tracepoint);
+ write_inferior_data_pointer (ipa_sym_addrs.addr_error_tracepoint, 0);
+
+ read_inferior_integer (ipa_sym_addrs.addr_expr_eval_result,
+ &ipa_expr_eval_result);
+ write_inferior_integer (ipa_sym_addrs.addr_expr_eval_result, 0);
+
+ trace_debug ("lib: trace_buffer_is_full: %d, "
+ "stopping_tracepoint: %s, "
+ "ipa_expr_eval_result: %d, "
+ "error_tracepoint: %s, ",
+ ipa_trace_buffer_is_full,
+ paddress (ipa_stopping_tracepoint),
+ ipa_expr_eval_result,
+ paddress (ipa_error_tracepoint));
+
+ if (debug_threads)
+ {
+ if (ipa_trace_buffer_is_full)
+ trace_debug ("lib stopped due to full buffer.");
+ if (ipa_stopping_tracepoint)
+ trace_debug ("lib stopped due to tpoint");
+ if (ipa_error_tracepoint)
+ trace_debug ("lib stopped due to error");
+ }
+
+ if (ipa_stopping_tracepoint != 0)
+ {
+ stopping_tracepoint
+ = fast_tracepoint_from_ipa_tpoint_address (ipa_stopping_tracepoint);
+ }
+ else if (ipa_expr_eval_result != expr_eval_no_error)
+ {
+ expr_eval_result = ipa_expr_eval_result;
+ error_tracepoint
+ = fast_tracepoint_from_ipa_tpoint_address (ipa_error_tracepoint);
+ }
+ stop_tracing ();
+ return 1;
+ }
+ else if (stop_pc == ipa_sym_addrs.addr_flush_trace_buffer)
+ {
+ trace_debug ("lib stopped at flush_trace_buffer");
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return true if TINFO just hit a tracepoint. Collect data if
+ so. */
+
+int
+tracepoint_was_hit (struct thread_info *tinfo, CORE_ADDR stop_pc)
+{
+ struct tracepoint *tpoint;
+ int ret = 0;
+ struct trap_tracepoint_ctx ctx;
+
+ /* Not tracing, don't handle. */
+ if (!tracing)
+ return 0;
+
+ ctx.base.type = trap_tracepoint;
+ ctx.regcache = get_thread_regcache (tinfo, 1);
+
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ {
+ /* Note that we collect fast tracepoints here as well. We'll
+ step over the fast tracepoint jump later, which avoids the
+ 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->id),
+ tpoint->number, paddress (tpoint->address));
+
+ /* Test the condition if present, and collect if true. */
+ if (!tpoint->cond
+ || (condition_true_at_tracepoint
+ ((struct tracepoint_hit_ctx *) &ctx, tpoint)))
+ collect_data_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
+ stop_pc, tpoint);
+
+ if (stopping_tracepoint
+ || trace_buffer_is_full
+ || expr_eval_result != expr_eval_no_error)
+ {
+ stop_tracing ();
+ }
+ /* If the tracepoint had a 'while-stepping' action, then set
+ the thread to collect this tracepoint on the following
+ single-steps. */
+ else if (tpoint->step_count > 0)
+ {
+ add_while_stepping_state (tinfo,
+ tpoint->number, tpoint->address);
+ }
+
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+#endif
+
+#if defined IN_PROCESS_AGENT && defined HAVE_UST
+struct ust_marker_data;
+static void collect_ust_data_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ struct traceframe *tframe);
+#endif
+
+/* Create a trace frame for the hit of the given tracepoint in the
+ given thread. */
+
+static void
+collect_data_at_tracepoint (struct tracepoint_hit_ctx *ctx, CORE_ADDR stop_pc,
+ struct tracepoint *tpoint)
+{
+ struct traceframe *tframe;
+ int acti;
+
+ /* Only count it as a hit when we actually collect data. */
+ tpoint->hit_count++;
+
+ /* If we've exceeded a defined pass count, record the event for
+ later, and finish the collection for this hit. This test is only
+ for nonstepping tracepoints, stepping tracepoints test at the end
+ of their while-stepping loop. */
+ if (tpoint->pass_count > 0
+ && tpoint->hit_count >= tpoint->pass_count
+ && tpoint->step_count == 0
+ && stopping_tracepoint == NULL)
+ stopping_tracepoint = tpoint;
+
+ 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);
+
+ if (tframe)
+ {
+ for (acti = 0; acti < tpoint->numactions; ++acti)
+ {
+#ifndef IN_PROCESS_AGENT
+ trace_debug ("Tracepoint %d at 0x%s about to do action '%s'",
+ tpoint->number, paddress (tpoint->address),
+ tpoint->actions_str[acti]);
+#endif
+
+ do_action_at_tracepoint (ctx, stop_pc, tpoint, tframe,
+ tpoint->actions[acti]);
+ }
+
+ finish_traceframe (tframe);
+ }
+
+ if (tframe == NULL && tracing)
+ trace_buffer_is_full = 1;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+static void
+collect_data_at_step (struct tracepoint_hit_ctx *ctx,
+ CORE_ADDR stop_pc,
+ struct tracepoint *tpoint, int current_step)
+{
+ struct traceframe *tframe;
+ int acti;
+
+ trace_debug ("Making new step traceframe for "
+ "tracepoint %d at 0x%s, step %d of %" PRIu64 ", hit %" PRIu64,
+ tpoint->number, paddress (tpoint->address),
+ current_step, tpoint->step_count,
+ tpoint->hit_count);
+
+ tframe = add_traceframe (tpoint);
+
+ if (tframe)
+ {
+ for (acti = 0; acti < tpoint->num_step_actions; ++acti)
+ {
+ trace_debug ("Tracepoint %d at 0x%s about to do step action '%s'",
+ tpoint->number, paddress (tpoint->address),
+ tpoint->step_actions_str[acti]);
+
+ do_action_at_tracepoint (ctx, stop_pc, tpoint, tframe,
+ tpoint->step_actions[acti]);
+ }
+
+ finish_traceframe (tframe);
+ }
+
+ if (tframe == NULL && tracing)
+ trace_buffer_is_full = 1;
+}
+
+#endif
+
+#ifdef IN_PROCESS_AGENT
+/* The target description index for IPA. Passed from gdbserver, used
+ to select ipa_tdesc. */
+EXTERN_C_PUSH
+IP_AGENT_EXPORT_VAR int ipa_tdesc_idx;
+EXTERN_C_POP
+#endif
+
+static struct regcache *
+get_context_regcache (struct tracepoint_hit_ctx *ctx)
+{
+ struct regcache *regcache = NULL;
+#ifdef IN_PROCESS_AGENT
+ const struct target_desc *ipa_tdesc = get_ipa_tdesc (ipa_tdesc_idx);
+
+ if (ctx->type == fast_tracepoint)
+ {
+ struct fast_tracepoint_ctx *fctx = (struct fast_tracepoint_ctx *) ctx;
+ if (!fctx->regcache_initted)
+ {
+ fctx->regcache_initted = 1;
+ init_register_cache (&fctx->regcache, ipa_tdesc, fctx->regspace);
+ supply_regblock (&fctx->regcache, NULL);
+ supply_fast_tracepoint_registers (&fctx->regcache, fctx->regs);
+ }
+ regcache = &fctx->regcache;
+ }
+#ifdef HAVE_UST
+ if (ctx->type == static_tracepoint)
+ {
+ struct static_tracepoint_ctx *sctx
+ = (struct static_tracepoint_ctx *) ctx;
+
+ if (!sctx->regcache_initted)
+ {
+ sctx->regcache_initted = 1;
+ init_register_cache (&sctx->regcache, ipa_tdesc, sctx->regspace);
+ supply_regblock (&sctx->regcache, NULL);
+ /* Pass down the tracepoint address, because REGS doesn't
+ include the PC, but we know what it must have been. */
+ supply_static_tracepoint_registers (&sctx->regcache,
+ (const unsigned char *)
+ sctx->regs,
+ sctx->tpoint->address);
+ }
+ regcache = &sctx->regcache;
+ }
+#endif
+#else
+ if (ctx->type == trap_tracepoint)
+ {
+ struct trap_tracepoint_ctx *tctx = (struct trap_tracepoint_ctx *) ctx;
+ regcache = tctx->regcache;
+ }
+#endif
+
+ gdb_assert (regcache != NULL);
+
+ return regcache;
+}
+
+static void
+do_action_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ CORE_ADDR stop_pc,
+ struct tracepoint *tpoint,
+ struct traceframe *tframe,
+ struct tracepoint_action *taction)
+{
+ enum eval_result_type err;
+
+ switch (taction->type)
+ {
+ 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 (&ax_ctx, NULL, (CORE_ADDR) maction->addr,
+ maction->len);
+ break;
+ }
+ case 'R':
+ {
+ unsigned char *regspace;
+ struct regcache tregcache;
+ struct regcache *context_regcache;
+ int regcache_size;
+
+ trace_debug ("Want to collect registers");
+
+ context_regcache = get_context_regcache (ctx);
+ regcache_size = register_cache_size (context_regcache->tdesc);
+
+ /* Collect all registers for now. */
+ regspace = add_traceframe_block (tframe, tpoint, 1 + regcache_size);
+ if (regspace == NULL)
+ {
+ trace_debug ("Trace buffer block allocation failed, skipping");
+ break;
+ }
+ /* Identify a register block. */
+ *regspace = 'R';
+
+ /* Wrap the regblock in a register cache (in the stack, we
+ don't want to malloc here). */
+ init_register_cache (&tregcache, context_regcache->tdesc,
+ regspace + 1);
+
+ /* Copy the register data to the regblock. */
+ regcache_cpy (&tregcache, context_regcache);
+
+#ifndef IN_PROCESS_AGENT
+ /* On some platforms, trap-based tracepoints will have the PC
+ pointing to the next instruction after the trap, but we
+ don't want the user or GDB trying to guess whether the
+ saved PC needs adjusting; so always record the adjusted
+ stop_pc. Note that we can't use tpoint->address instead,
+ since it will be wrong for while-stepping actions. This
+ adjustment is a nop for fast tracepoints collected from the
+ in-process lib (but not if GDBserver is collecting one
+ 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 (stop_pc));
+
+ /* This changes the regblock, not the thread's
+ regcache. */
+ regcache_write_pc (&tregcache, stop_pc);
+#endif
+ }
+ break;
+ 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 = gdb_eval_agent_expr (&ax_ctx, eaction->expr, NULL);
+
+ if (err != expr_eval_no_error)
+ {
+ record_tracepoint_error (tpoint, "action expression", err);
+ return;
+ }
+ }
+ break;
+ case 'L':
+ {
+#if defined IN_PROCESS_AGENT && defined HAVE_UST
+ trace_debug ("Want to collect static trace data");
+ collect_ust_data_at_tracepoint (ctx, tframe);
+#else
+ trace_debug ("warning: collecting static trace data, "
+ "but static tracepoints are not supported");
+#endif
+ }
+ break;
+ default:
+ trace_debug ("unknown trace action '%c', ignoring", taction->type);
+ break;
+ }
+}
+
+static int
+condition_true_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ struct tracepoint *tpoint)
+{
+ ULONGEST value = 0;
+ enum eval_result_type err;
+
+ /* Presently, gdbserver doesn't run compiled conditions, only the
+ IPA does. If the program stops at a fast tracepoint's address
+ (e.g., due to a breakpoint, trap tracepoint, or stepping),
+ gdbserver preemptively collect the fast tracepoint. Later, on
+ resume, gdbserver steps over the fast tracepoint like it steps
+ over breakpoints, so that the IPA doesn't see that fast
+ tracepoint. This avoids double collects of fast tracepoints in
+ that stopping scenario. Having gdbserver itself handle the fast
+ tracepoint gives the user a consistent view of when fast or trap
+ tracepoints are collected, compared to an alternative where only
+ trap tracepoints are collected on stop, and fast tracepoints on
+ resume. When a fast tracepoint is being processed by gdbserver,
+ it is always the non-compiled condition expression that is
+ used. */
+#ifdef IN_PROCESS_AGENT
+ if (tpoint->compiled_cond)
+ {
+ struct fast_tracepoint_ctx *fctx = (struct fast_tracepoint_ctx *) ctx;
+ err = ((condfn) (uintptr_t) (tpoint->compiled_cond)) (fctx->regs, &value);
+ }
+ else
+#endif
+ {
+ 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);
+ /* The error case must return false. */
+ return 0;
+ }
+
+ trace_debug ("Tracepoint %d at 0x%s condition evals to %s",
+ tpoint->number, paddress (tpoint->address),
+ pulongest (value));
+ return (value ? 1 : 0);
+}
+
+/* Do memory copies for bytecodes. */
+/* Do the recording of memory blocks for actions and bytecodes. */
+
+int
+agent_mem_read (struct eval_agent_expr_context *ctx,
+ 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)
+ {
+ read_inferior_memory (from, to, len);
+ return 0;
+ }
+
+ /* Otherwise, create a new memory block in the trace buffer. */
+ while (remaining > 0)
+ {
+ size_t sp;
+
+ blocklen = (remaining > 65535 ? 65535 : remaining);
+ sp = 1 + sizeof (from) + sizeof (blocklen) + blocklen;
+ mspace = add_traceframe_block (ctx->tframe, ctx->tpoint, sp);
+ if (mspace == NULL)
+ return 1;
+ /* Identify block as a memory block. */
+ *mspace = 'M';
+ ++mspace;
+ /* Record address and size. */
+ memcpy (mspace, &from, sizeof (from));
+ mspace += sizeof (from);
+ memcpy (mspace, &blocklen, sizeof (blocklen));
+ mspace += sizeof (blocklen);
+ /* Record the memory block proper. */
+ read_inferior_memory (from, mspace, blocklen);
+ trace_debug ("%d bytes recorded", blocklen);
+ remaining -= blocklen;
+ from += blocklen;
+ }
+ 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. */
+
+int
+agent_tsv_read (struct eval_agent_expr_context *ctx, int n)
+{
+ unsigned char *vspace;
+ LONGEST val;
+
+ vspace = add_traceframe_block (ctx->tframe, ctx->tpoint,
+ 1 + sizeof (n) + sizeof (LONGEST));
+ if (vspace == NULL)
+ return 1;
+ /* Identify block as a variable. */
+ *vspace = 'V';
+ /* Record variable's number and value. */
+ memcpy (vspace + 1, &n, sizeof (n));
+ val = get_trace_state_variable_value (n);
+ memcpy (vspace + 1 + sizeof (n), &val, sizeof (val));
+ trace_debug ("Variable %d recorded", n);
+ return 0;
+}
+
+#ifndef IN_PROCESS_AGENT
+
+/* Callback for traceframe_walk_blocks, used to find a given block
+ type in a traceframe. */
+
+static int
+match_blocktype (char blocktype, unsigned char *dataptr, void *data)
+{
+ char *wantedp = (char *) data;
+
+ if (*wantedp == blocktype)
+ return 1;
+
+ return 0;
+}
+
+/* Walk over all traceframe blocks of the traceframe buffer starting
+ at DATABASE, of DATASIZE bytes long, and call CALLBACK for each
+ block found, passing in DATA unmodified. If CALLBACK returns true,
+ this returns a pointer to where the block is found. Returns NULL
+ if no callback call returned true, indicating that all blocks have
+ been walked. */
+
+static unsigned char *
+traceframe_walk_blocks (unsigned char *database, unsigned int datasize,
+ int tfnum,
+ int (*callback) (char blocktype,
+ unsigned char *dataptr,
+ void *data),
+ void *data)
+{
+ unsigned char *dataptr;
+
+ if (datasize == 0)
+ {
+ trace_debug ("traceframe %d has no data", tfnum);
+ return NULL;
+ }
+
+ /* Iterate through a traceframe's blocks, looking for a block of the
+ requested type. */
+ for (dataptr = database;
+ dataptr < database + datasize;
+ /* nothing */)
+ {
+ char blocktype;
+ unsigned short mlen;
+
+ if (dataptr == trace_buffer_wrap)
+ {
+ /* Adjust to reflect wrapping part of the frame around to
+ the beginning. */
+ datasize = dataptr - database;
+ dataptr = database = trace_buffer_lo;
+ }
+
+ blocktype = *dataptr++;
+
+ if ((*callback) (blocktype, dataptr, data))
+ return dataptr;
+
+ switch (blocktype)
+ {
+ case 'R':
+ /* Skip over the registers block. */
+ dataptr += current_target_desc ()->registers_size;
+ break;
+ case 'M':
+ /* Skip over the memory block. */
+ dataptr += sizeof (CORE_ADDR);
+ memcpy (&mlen, dataptr, sizeof (mlen));
+ dataptr += (sizeof (mlen) + mlen);
+ break;
+ case 'V':
+ /* Skip over the TSV block. */
+ dataptr += (sizeof (int) + sizeof (LONGEST));
+ break;
+ case 'S':
+ /* Skip over the static trace data block. */
+ memcpy (&mlen, dataptr, sizeof (mlen));
+ dataptr += (sizeof (mlen) + mlen);
+ break;
+ default:
+ trace_debug ("traceframe %d has unknown block type 0x%x",
+ tfnum, blocktype);
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+/* Look for the block of type TYPE_WANTED in the traceframe starting
+ at DATABASE of DATASIZE bytes long. TFNUM is the traceframe
+ number. */
+
+static unsigned char *
+traceframe_find_block_type (unsigned char *database, unsigned int datasize,
+ int tfnum, char type_wanted)
+{
+ return traceframe_walk_blocks (database, datasize, tfnum,
+ match_blocktype, &type_wanted);
+}
+
+static unsigned char *
+traceframe_find_regblock (struct traceframe *tframe, int tfnum)
+{
+ unsigned char *regblock;
+
+ regblock = traceframe_find_block_type (tframe->data,
+ tframe->data_size,
+ tfnum, 'R');
+
+ if (regblock == NULL)
+ trace_debug ("traceframe %d has no register data", tfnum);
+
+ return regblock;
+}
+
+/* Get registers from a traceframe. */
+
+int
+fetch_traceframe_registers (int tfnum, struct regcache *regcache, int regnum)
+{
+ unsigned char *dataptr;
+ struct tracepoint *tpoint;
+ struct traceframe *tframe;
+
+ tframe = find_traceframe (tfnum);
+
+ if (tframe == NULL)
+ {
+ trace_debug ("traceframe %d not found", tfnum);
+ return 1;
+ }
+
+ dataptr = traceframe_find_regblock (tframe, tfnum);
+ if (dataptr == NULL)
+ {
+ /* Mark registers unavailable. */
+ supply_regblock (regcache, NULL);
+
+ /* We can generally guess at a PC, although this will be
+ misleading for while-stepping frames and multi-location
+ tracepoints. */
+ tpoint = find_next_tracepoint_by_number (NULL, tframe->tpnum);
+ if (tpoint != NULL)
+ regcache_write_pc (regcache, tpoint->address);
+ }
+ else
+ supply_regblock (regcache, dataptr);
+
+ return 0;
+}
+
+static CORE_ADDR
+traceframe_get_pc (struct traceframe *tframe)
+{
+ struct regcache regcache;
+ unsigned char *dataptr;
+ const struct target_desc *tdesc = current_target_desc ();
+
+ dataptr = traceframe_find_regblock (tframe, -1);
+ if (dataptr == NULL)
+ return 0;
+
+ init_register_cache (&regcache, tdesc, dataptr);
+ return regcache_read_pc (&regcache);
+}
+
+/* Read a requested block of memory from a trace frame. */
+
+int
+traceframe_read_mem (int tfnum, CORE_ADDR addr,
+ unsigned char *buf, ULONGEST length,
+ ULONGEST *nbytes)
+{
+ struct traceframe *tframe;
+ unsigned char *database, *dataptr;
+ unsigned int datasize;
+ CORE_ADDR maddr;
+ unsigned short mlen;
+
+ trace_debug ("traceframe_read_mem");
+
+ tframe = find_traceframe (tfnum);
+
+ if (!tframe)
+ {
+ trace_debug ("traceframe %d not found", tfnum);
+ return 1;
+ }
+
+ datasize = tframe->data_size;
+ database = dataptr = &tframe->data[0];
+
+ /* Iterate through a traceframe's blocks, looking for memory. */
+ while ((dataptr = traceframe_find_block_type (dataptr,
+ datasize
+ - (dataptr - database),
+ tfnum, 'M')) != NULL)
+ {
+ memcpy (&maddr, dataptr, sizeof (maddr));
+ dataptr += sizeof (maddr);
+ memcpy (&mlen, dataptr, sizeof (mlen));
+ dataptr += sizeof (mlen);
+ trace_debug ("traceframe %d has %d bytes at %s",
+ tfnum, mlen, paddress (maddr));
+
+ /* If the block includes the first part of the desired range,
+ return as much it has; GDB will re-request the remainder,
+ which might be in a different block of this trace frame. */
+ if (maddr <= addr && addr < (maddr + mlen))
+ {
+ ULONGEST amt = (maddr + mlen) - addr;
+ if (amt > length)
+ amt = length;
+
+ memcpy (buf, dataptr + (addr - maddr), amt);
+ *nbytes = amt;
+ return 0;
+ }
+
+ /* Skip over this block. */
+ dataptr += mlen;
+ }
+
+ trace_debug ("traceframe %d has no memory data for the desired region",
+ tfnum);
+
+ *nbytes = 0;
+ return 0;
+}
+
+static int
+traceframe_read_tsv (int tsvnum, LONGEST *val)
+{
+ client_state &cs = get_client_state ();
+ int tfnum;
+ struct traceframe *tframe;
+ unsigned char *database, *dataptr;
+ unsigned int datasize;
+ int vnum;
+ int found = 0;
+
+ trace_debug ("traceframe_read_tsv");
+
+ tfnum = cs.current_traceframe;
+
+ if (tfnum < 0)
+ {
+ trace_debug ("no current traceframe");
+ return 1;
+ }
+
+ tframe = find_traceframe (tfnum);
+
+ if (tframe == NULL)
+ {
+ trace_debug ("traceframe %d not found", tfnum);
+ return 1;
+ }
+
+ datasize = tframe->data_size;
+ database = dataptr = &tframe->data[0];
+
+ /* Iterate through a traceframe's blocks, looking for the last
+ matched tsv. */
+ while ((dataptr = traceframe_find_block_type (dataptr,
+ datasize
+ - (dataptr - database),
+ tfnum, 'V')) != NULL)
+ {
+ memcpy (&vnum, dataptr, sizeof (vnum));
+ dataptr += sizeof (vnum);
+
+ trace_debug ("traceframe %d has variable %d", tfnum, vnum);
+
+ /* Check that this is the variable we want. */
+ if (tsvnum == vnum)
+ {
+ memcpy (val, dataptr, sizeof (*val));
+ found = 1;
+ }
+
+ /* Skip over this block. */
+ dataptr += sizeof (LONGEST);
+ }
+
+ if (!found)
+ trace_debug ("traceframe %d has no data for variable %d",
+ tfnum, tsvnum);
+ return !found;
+}
+
+/* Read a requested block of static tracepoint data from a trace
+ frame. */
+
+int
+traceframe_read_sdata (int tfnum, ULONGEST offset,
+ unsigned char *buf, ULONGEST length,
+ ULONGEST *nbytes)
+{
+ struct traceframe *tframe;
+ unsigned char *database, *dataptr;
+ unsigned int datasize;
+ unsigned short mlen;
+
+ trace_debug ("traceframe_read_sdata");
+
+ tframe = find_traceframe (tfnum);
+
+ if (!tframe)
+ {
+ trace_debug ("traceframe %d not found", tfnum);
+ return 1;
+ }
+
+ datasize = tframe->data_size;
+ database = &tframe->data[0];
+
+ /* Iterate through a traceframe's blocks, looking for static
+ tracepoint data. */
+ dataptr = traceframe_find_block_type (database, datasize,
+ tfnum, 'S');
+ if (dataptr != NULL)
+ {
+ memcpy (&mlen, dataptr, sizeof (mlen));
+ dataptr += sizeof (mlen);
+ if (offset < mlen)
+ {
+ if (offset + length > mlen)
+ length = mlen - offset;
+
+ memcpy (buf, dataptr, length);
+ *nbytes = length;
+ }
+ else
+ *nbytes = 0;
+ return 0;
+ }
+
+ trace_debug ("traceframe %d has no static trace data", tfnum);
+
+ *nbytes = 0;
+ return 0;
+}
+
+/* Callback for traceframe_walk_blocks. Builds a traceframe-info
+ object. DATA is pointer to a struct buffer holding the
+ traceframe-info object being built. */
+
+static int
+build_traceframe_info_xml (char blocktype, unsigned char *dataptr, void *data)
+{
+ struct buffer *buffer = (struct buffer *) data;
+
+ switch (blocktype)
+ {
+ case 'M':
+ {
+ unsigned short mlen;
+ CORE_ADDR maddr;
+
+ memcpy (&maddr, dataptr, sizeof (maddr));
+ dataptr += sizeof (maddr);
+ memcpy (&mlen, dataptr, sizeof (mlen));
+ dataptr += sizeof (mlen);
+ buffer_xml_printf (buffer,
+ "<memory start=\"0x%s\" length=\"0x%s\"/>\n",
+ paddress (maddr), phex_nz (mlen, sizeof (mlen)));
+ break;
+ }
+ case 'V':
+ {
+ int vnum;
+
+ memcpy (&vnum, dataptr, sizeof (vnum));
+ buffer_xml_printf (buffer, "<tvar id=\"%d\"/>\n", vnum);
+ break;
+ }
+ case 'R':
+ case 'S':
+ {
+ break;
+ }
+ default:
+ warning ("Unhandled trace block type (%d) '%c ' "
+ "while building trace frame info.",
+ blocktype, blocktype);
+ break;
+ }
+
+ return 0;
+}
+
+/* Build a traceframe-info object for traceframe number TFNUM into
+ BUFFER. */
+
+int
+traceframe_read_info (int tfnum, struct buffer *buffer)
+{
+ struct traceframe *tframe;
+
+ trace_debug ("traceframe_read_info");
+
+ tframe = find_traceframe (tfnum);
+
+ if (!tframe)
+ {
+ trace_debug ("traceframe %d not found", tfnum);
+ return 1;
+ }
+
+ buffer_grow_str (buffer, "<traceframe-info>\n");
+ traceframe_walk_blocks (tframe->data, tframe->data_size,
+ tfnum, build_traceframe_info_xml, buffer);
+ buffer_grow_str0 (buffer, "</traceframe-info>\n");
+ return 0;
+}
+
+/* Return the first fast tracepoint whose jump pad contains PC. */
+
+static struct tracepoint *
+fast_tracepoint_from_jump_pad_address (CORE_ADDR pc)
+{
+ struct tracepoint *tpoint;
+
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ if (tpoint->type == fast_tracepoint)
+ if (tpoint->jump_pad <= pc && pc < tpoint->jump_pad_end)
+ return tpoint;
+
+ 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. */
+
+static struct tracepoint *
+fast_tracepoint_from_ipa_tpoint_address (CORE_ADDR ipa_tpoint_obj)
+{
+ struct tracepoint *tpoint;
+
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ if (tpoint->type == fast_tracepoint)
+ if (tpoint->obj_addr_on_target == ipa_tpoint_obj)
+ return tpoint;
+
+ return NULL;
+}
+
+#endif
+
+/* The type of the object that is used to synchronize fast tracepoint
+ collection. */
+
+typedef struct collecting_t
+{
+ /* The fast tracepoint number currently collecting. */
+ uintptr_t tpoint;
+
+ /* A number that GDBserver can use to identify the thread that is
+ presently holding the collect lock. This need not (and usually
+ is not) the thread id, as getting the current thread ID usually
+ requires a system call, which we want to avoid like the plague.
+ Usually this is thread's TCB, found in the TLS (pseudo-)
+ register, which is readable with a single insn on several
+ architectures. */
+ uintptr_t thread_area;
+} collecting_t;
+
+#ifndef IN_PROCESS_AGENT
+
+void
+force_unlock_trace_buffer (void)
+{
+ write_inferior_data_pointer (ipa_sym_addrs.addr_collecting, 0);
+}
+
+/* Check if the thread identified by THREAD_AREA which is stopped at
+ STOP_PC, is presently locking the fast tracepoint collection, and
+ if so, gather some status of said collection. Returns 0 if the
+ thread isn't collecting or in the jump pad at all. 1, if in the
+ jump pad (or within gdb_collect) and hasn't executed the adjusted
+ original insn yet (can set a breakpoint there and run to it). 2,
+ if presently executing the adjusted original insn --- in which
+ case, if we want to move the thread out of the jump pad, we need to
+ single-step it until this function returns 0. */
+
+fast_tpoint_collect_result
+fast_tracepoint_collecting (CORE_ADDR thread_area,
+ CORE_ADDR stop_pc,
+ struct fast_tpoint_collect_status *status)
+{
+ 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;
+
+ /* The thread THREAD_AREA is either:
+
+ 0. not collecting at all, not within the jump pad, or within
+ gdb_collect or one of its callees.
+
+ 1. in the jump pad and haven't reached gdb_collect
+
+ 2. within gdb_collect (out of the jump pad) (collect is set)
+
+ 3. we're in the jump pad, after gdb_collect having returned,
+ possibly executing the adjusted insns.
+
+ For cases 1 and 3, `collecting' may or not be set. The jump pad
+ doesn't have any complicated jump logic, so we can tell if the
+ thread is executing the adjust original insn or not by just
+ matching STOP_PC with known jump pad addresses. If we it isn't
+ yet executing the original insn, set a breakpoint there, and let
+ the thread run to it, so to quickly step over a possible (many
+ insns) gdb_collect call. Otherwise, or when the breakpoint is
+ hit, only a few (small number of) insns are left to be executed
+ in the jump pad. Single-step the thread until it leaves the
+ jump pad. */
+
+ again:
+ tpoint = NULL;
+ needs_breakpoint = 0;
+ trace_debug ("fast_tracepoint_collecting");
+
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_jump_pad_buffer,
+ &ipa_gdb_jump_pad_buffer))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting `gdb_jump_pad_buffer'");
+ }
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_jump_pad_buffer_end,
+ &ipa_gdb_jump_pad_buffer_end))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting `gdb_jump_pad_buffer_end'");
+ }
+
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer,
+ &ipa_gdb_trampoline_buffer))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting `gdb_trampoline_buffer'");
+ }
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_end,
+ &ipa_gdb_trampoline_buffer_end))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error extracting `gdb_trampoline_buffer_end'");
+ }
+
+ if (ipa_gdb_jump_pad_buffer <= stop_pc
+ && stop_pc < ipa_gdb_jump_pad_buffer_end)
+ {
+ /* We can tell which tracepoint(s) the thread is collecting by
+ matching the jump pad address back to the tracepoint. */
+ tpoint = fast_tracepoint_from_jump_pad_address (stop_pc);
+ if (tpoint == NULL)
+ {
+ warning ("in jump pad, but no matching tpoint?");
+ return fast_tpoint_collect_result::not_collecting;
+ }
+ else
+ {
+ trace_debug ("in jump pad of tpoint (%d, %s); jump_pad(%s, %s); "
+ "adj_insn(%s, %s)",
+ tpoint->number, paddress (tpoint->address),
+ paddress (tpoint->jump_pad),
+ paddress (tpoint->jump_pad_end),
+ paddress (tpoint->adjusted_insn_addr),
+ paddress (tpoint->adjusted_insn_addr_end));
+ }
+
+ /* Definitely in the jump pad. May or may not need
+ fast-exit-jump-pad breakpoint. */
+ if (tpoint->jump_pad <= stop_pc
+ && 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 fast_tpoint_collect_result::not_collecting;
+ }
+ 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;
+
+ /* If `collecting' is set/locked, then the THREAD_AREA thread
+ may or not be the one holding the lock. We have to read the
+ lock to find out. */
+
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_collecting,
+ &ipa_collecting))
+ {
+ trace_debug ("fast_tracepoint_collecting:"
+ " failed reading 'collecting' in the inferior");
+ return fast_tpoint_collect_result::not_collecting;
+ }
+
+ if (!ipa_collecting)
+ {
+ trace_debug ("fast_tracepoint_collecting: not collecting"
+ " (and nobody is).");
+ return fast_tpoint_collect_result::not_collecting;
+ }
+
+ /* Some thread is collecting. Check which. */
+ if (read_inferior_memory (ipa_collecting,
+ (unsigned char *) &ipa_collecting_obj,
+ sizeof (ipa_collecting_obj)) != 0)
+ goto again;
+
+ if (ipa_collecting_obj.thread_area != thread_area)
+ {
+ trace_debug ("fast_tracepoint_collecting: not collecting "
+ "(another thread is)");
+ return fast_tpoint_collect_result::not_collecting;
+ }
+
+ tpoint
+ = fast_tracepoint_from_ipa_tpoint_address (ipa_collecting_obj.tpoint);
+ if (tpoint == NULL)
+ {
+ warning ("fast_tracepoint_collecting: collecting, "
+ "but tpoint %s not found?",
+ paddress ((CORE_ADDR) ipa_collecting_obj.tpoint));
+ return fast_tpoint_collect_result::not_collecting;
+ }
+
+ /* The thread is within `gdb_collect', skip over the rest of
+ fast tracepoint collection quickly using a breakpoint. */
+ needs_breakpoint = 1;
+ }
+
+ /* The caller wants a bit of status detail. */
+ if (status != NULL)
+ {
+ status->tpoint_num = tpoint->number;
+ status->tpoint_addr = tpoint->address;
+ status->adjusted_insn_addr = tpoint->adjusted_insn_addr;
+ status->adjusted_insn_addr_end = tpoint->adjusted_insn_addr_end;
+ }
+
+ if (needs_breakpoint)
+ {
+ /* Hasn't executed the original instruction yet. Set breakpoint
+ there, and wait till it's hit, then single-step until exiting
+ the jump pad. */
+
+ trace_debug ("\
+fast_tracepoint_collecting, returning continue-until-break at %s",
+ paddress (tpoint->adjusted_insn_addr));
+
+ return fast_tpoint_collect_result::before_insn; /* continue */
+ }
+ else
+ {
+ /* Just single-step until exiting the jump pad. */
+
+ trace_debug ("fast_tracepoint_collecting, returning "
+ "need-single-step (%s-%s)",
+ paddress (tpoint->adjusted_insn_addr),
+ paddress (tpoint->adjusted_insn_addr_end));
+
+ return fast_tpoint_collect_result::at_insn; /* single-step */
+ }
+}
+
+#endif
+
+#ifdef IN_PROCESS_AGENT
+
+/* The global fast tracepoint collect lock. Points to a collecting_t
+ object built on the stack by the jump pad, if presently locked;
+ NULL if it isn't locked. Note that this lock *must* be set while
+ executing any *function other than the jump pad. See
+ fast_tracepoint_collecting. */
+EXTERN_C_PUSH
+IP_AGENT_EXPORT_VAR collecting_t *collecting;
+EXTERN_C_POP
+
+/* This is needed for -Wmissing-declarations. */
+IP_AGENT_EXPORT_FUNC void gdb_collect (struct tracepoint *tpoint,
+ unsigned char *regs);
+
+/* This routine, called from the jump pad (in asm) is designed to be
+ called from the jump pads of fast tracepoints, thus it is on the
+ critical path. */
+
+IP_AGENT_EXPORT_FUNC void
+gdb_collect (struct tracepoint *tpoint, unsigned char *regs)
+{
+ struct fast_tracepoint_ctx ctx;
+ const struct target_desc *ipa_tdesc;
+
+ /* Don't do anything until the trace run is completely set up. */
+ if (!tracing)
+ return;
+
+ ipa_tdesc = get_ipa_tdesc (ipa_tdesc_idx);
+ ctx.base.type = fast_tracepoint;
+ ctx.regs = regs;
+ ctx.regcache_initted = 0;
+ /* Wrap the regblock in a register cache (in the stack, we don't
+ want to malloc here). */
+ ctx.regspace = (unsigned char *) alloca (ipa_tdesc->registers_size);
+ if (ctx.regspace == NULL)
+ {
+ trace_debug ("Trace buffer block allocation failed, skipping");
+ return;
+ }
+
+ for (ctx.tpoint = tpoint;
+ ctx.tpoint != NULL && ctx.tpoint->address == tpoint->address;
+ ctx.tpoint = ctx.tpoint->next)
+ {
+ if (!ctx.tpoint->enabled)
+ continue;
+
+ /* 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;
+ }
+ }
+ }
+}
+
+/* These global variables points to the corresponding functions. This is
+ necessary on powerpc64, where asking for function symbol address from gdb
+ results in returning the actual code pointer, instead of the descriptor
+ pointer. */
+
+typedef void (*gdb_collect_ptr_type) (struct tracepoint *, unsigned char *);
+typedef ULONGEST (*get_raw_reg_ptr_type) (const unsigned char *, int);
+typedef LONGEST (*get_trace_state_variable_value_ptr_type) (int);
+typedef void (*set_trace_state_variable_value_ptr_type) (int, LONGEST);
+
+EXTERN_C_PUSH
+IP_AGENT_EXPORT_VAR gdb_collect_ptr_type gdb_collect_ptr = gdb_collect;
+IP_AGENT_EXPORT_VAR get_raw_reg_ptr_type get_raw_reg_ptr = get_raw_reg;
+IP_AGENT_EXPORT_VAR get_trace_state_variable_value_ptr_type
+ get_trace_state_variable_value_ptr = get_trace_state_variable_value;
+IP_AGENT_EXPORT_VAR set_trace_state_variable_value_ptr_type
+ set_trace_state_variable_value_ptr = set_trace_state_variable_value;
+EXTERN_C_POP
+
+#endif
+
+#ifndef IN_PROCESS_AGENT
+
+CORE_ADDR
+get_raw_reg_func_addr (void)
+{
+ CORE_ADDR res;
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_get_raw_reg_ptr, &res))
+ {
+ error ("error extracting get_raw_reg_ptr");
+ return 0;
+ }
+ return res;
+}
+
+CORE_ADDR
+get_get_tsv_func_addr (void)
+{
+ CORE_ADDR res;
+ if (read_inferior_data_pointer (
+ ipa_sym_addrs.addr_get_trace_state_variable_value_ptr, &res))
+ {
+ error ("error extracting get_trace_state_variable_value_ptr");
+ return 0;
+ }
+ return res;
+}
+
+CORE_ADDR
+get_set_tsv_func_addr (void)
+{
+ CORE_ADDR res;
+ if (read_inferior_data_pointer (
+ ipa_sym_addrs.addr_set_trace_state_variable_value_ptr, &res))
+ {
+ error ("error extracting set_trace_state_variable_value_ptr");
+ return 0;
+ }
+ return res;
+}
+
+static void
+compile_tracepoint_condition (struct tracepoint *tpoint,
+ CORE_ADDR *jump_entry)
+{
+ CORE_ADDR entry_point = *jump_entry;
+ enum eval_result_type err;
+
+ trace_debug ("Starting condition compilation for tracepoint %d\n",
+ tpoint->number);
+
+ /* Initialize the global pointer to the code being built. */
+ current_insn_ptr = *jump_entry;
+
+ emit_prologue ();
+
+ err = compile_bytecodes (tpoint->cond);
+
+ if (err == expr_eval_no_error)
+ {
+ emit_epilogue ();
+
+ /* Record the beginning of the compiled code. */
+ tpoint->compiled_cond = entry_point;
+
+ trace_debug ("Condition compilation for tracepoint %d complete\n",
+ tpoint->number);
+ }
+ else
+ {
+ /* Leave the unfinished code in situ, but don't point to it. */
+
+ tpoint->compiled_cond = 0;
+
+ trace_debug ("Condition compilation for tracepoint %d failed, "
+ "error code %d",
+ tpoint->number, err);
+ }
+
+ /* Update the code pointer passed in. Note that we do this even if
+ the compile fails, so that we can look at the partial results
+ instead of letting them be overwritten. */
+ *jump_entry = current_insn_ptr;
+
+ /* Leave a gap, to aid dump decipherment. */
+ *jump_entry += 16;
+}
+
+/* The base pointer of the IPA's heap. This is the only memory the
+ IPA is allowed to use. The IPA should _not_ call the inferior's
+ `malloc' during operation. That'd be slow, and, most importantly,
+ it may not be safe. We may be collecting a tracepoint in a signal
+ handler, for example. */
+static CORE_ADDR target_tp_heap;
+
+/* Allocate at least SIZE bytes of memory from the IPA heap, aligned
+ to 8 bytes. */
+
+static CORE_ADDR
+target_malloc (ULONGEST size)
+{
+ CORE_ADDR ptr;
+
+ if (target_tp_heap == 0)
+ {
+ /* We have the pointer *address*, need what it points to. */
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_tp_heap_buffer,
+ &target_tp_heap))
+ {
+ internal_error (__FILE__, __LINE__,
+ "couldn't get target heap head pointer");
+ }
+ }
+
+ ptr = target_tp_heap;
+ target_tp_heap += size;
+
+ /* Pad to 8-byte alignment. */
+ target_tp_heap = ((target_tp_heap + 7) & ~0x7);
+
+ return ptr;
+}
+
+static CORE_ADDR
+download_agent_expr (struct agent_expr *expr)
+{
+ CORE_ADDR expr_addr;
+ CORE_ADDR expr_bytes;
+
+ expr_addr = target_malloc (sizeof (*expr));
+ target_write_memory (expr_addr, (unsigned char *) expr, sizeof (*expr));
+
+ expr_bytes = target_malloc (expr->length);
+ write_inferior_data_pointer (expr_addr + offsetof (struct agent_expr, bytes),
+ expr_bytes);
+ target_write_memory (expr_bytes, expr->bytes, expr->length);
+
+ return expr_addr;
+}
+
+/* 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;
+
+ target_write_memory (tpptr, (unsigned char *) &target_tracepoint,
+ sizeof (target_tracepoint));
+
+ if (tpoint->cond)
+ write_inferior_data_pointer (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_pointer (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 = tracepoint_action_download (action);
+
+ if (ipa_action != 0)
+ write_inferior_data_pointer (actions_array
+ + i * 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
+
+/* Send a command to agent to download and install tracepoint TPOINT. */
+
+static int
+tracepoint_send_agent (struct tracepoint *tpoint)
+{
+ char buf[IPA_CMD_BUF_SIZE];
+ char *p;
+ int i, ret;
+
+ p = buf;
+ strcpy (p, "FastTrace:");
+ p += 10;
+
+ 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_action *action = tpoint->actions[i];
+
+ p[0] = action->type;
+ p = tracepoint_action_send (&p[1], action);
+ }
+
+ 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;
+ }
+
+ ret = run_inferior_command (buf, (int) (ptrdiff_t) (p - buf));
+ if (ret)
+ return ret;
+
+ if (!startswith (buf, "OK"))
+ return 1;
+
+ /* 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);
+
+ if (tpoint->type == fast_tracepoint)
+ {
+ unsigned char *insn
+ = (unsigned char *) &buf[IPA_PROTO_FAST_TRACE_FJUMP_INSN];
+ int fjump_size;
+
+ 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);
+
+ memcpy (&gdb_jump_pad_head, &buf[IPA_PROTO_FAST_TRACE_JUMP_PAD], 8);
+
+ /* 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);
+ }
+
+ return 0;
+}
+
+static void
+download_tracepoint (struct tracepoint *tpoint)
+{
+ struct tracepoint *tp, *tp_prev;
+
+ 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))
+ {
+ internal_error (__FILE__, __LINE__,
+ "error reading `tp_prev->next'");
+ }
+
+ /* tpoint->next = tp_prev->next */
+ write_inferior_data_pointer (tpoint->obj_addr_on_target
+ + offsetof (struct tracepoint, next),
+ tp_prev_target_next_addr);
+ /* tp_prev->next = tpoint */
+ write_inferior_data_pointer (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_pointer (ipa_sym_addrs.addr_tracepoints,
+ tpoint->obj_addr_on_target);
+
+}
+
+static void
+download_trace_state_variables (void)
+{
+ CORE_ADDR ptr = 0, prev_ptr = 0;
+ struct trace_state_variable *tsv;
+
+ /* Start out empty. */
+ write_inferior_data_pointer (ipa_sym_addrs.addr_trace_state_variables, 0);
+
+ for (tsv = trace_state_variables; tsv != NULL; tsv = tsv->next)
+ {
+ struct trace_state_variable target_tsv;
+
+ /* TSV's with a getter have been initialized equally in both the
+ inferior and GDBserver. Skip them. */
+ if (tsv->getter != NULL)
+ continue;
+
+ target_tsv = *tsv;
+
+ prev_ptr = ptr;
+ ptr = target_malloc (sizeof (*tsv));
+
+ if (tsv == trace_state_variables)
+ {
+ /* First object in list, set the head pointer in the
+ inferior. */
+
+ write_inferior_data_pointer (ipa_sym_addrs.addr_trace_state_variables,
+ ptr);
+ }
+ else
+ {
+ write_inferior_data_pointer (prev_ptr
+ + offsetof (struct trace_state_variable,
+ next),
+ ptr);
+ }
+
+ /* Write the whole object. We'll fix up its pointers in a bit.
+ Assume no next, fixup when needed. */
+ target_tsv.next = NULL;
+
+ target_write_memory (ptr, (unsigned char *) &target_tsv,
+ sizeof (target_tsv));
+
+ if (tsv->name != NULL)
+ {
+ size_t size = strlen (tsv->name) + 1;
+ CORE_ADDR name_addr = target_malloc (size);
+ target_write_memory (name_addr,
+ (unsigned char *) tsv->name, size);
+ write_inferior_data_pointer (ptr
+ + offsetof (struct trace_state_variable,
+ name),
+ name_addr);
+ }
+
+ gdb_assert (tsv->getter == NULL);
+ }
+
+ if (prev_ptr != 0)
+ {
+ /* Fixup the next pointer in the last item in the list. */
+ write_inferior_data_pointer (prev_ptr
+ + offsetof (struct trace_state_variable,
+ next), 0);
+ }
+}
+
+/* Upload complete trace frames out of the IP Agent's trace buffer
+ into GDBserver's trace buffer. This always uploads either all or
+ no trace frames. This is the counter part of
+ `trace_alloc_trace_buffer'. See its description of the atomic
+ syncing mechanism. */
+
+static void
+upload_fast_traceframes (void)
+{
+ unsigned int ipa_traceframe_read_count, ipa_traceframe_write_count;
+ unsigned int ipa_traceframe_read_count_racy, ipa_traceframe_write_count_racy;
+ CORE_ADDR tf;
+ struct ipa_trace_buffer_control ipa_trace_buffer_ctrl;
+ unsigned int curr_tbctrl_idx;
+ unsigned int ipa_trace_buffer_ctrl_curr;
+ unsigned int ipa_trace_buffer_ctrl_curr_old;
+ CORE_ADDR ipa_trace_buffer_ctrl_addr;
+ struct breakpoint *about_to_request_buffer_space_bkpt;
+ CORE_ADDR ipa_trace_buffer_lo;
+ CORE_ADDR ipa_trace_buffer_hi;
+
+ if (read_inferior_uinteger (ipa_sym_addrs.addr_traceframe_read_count,
+ &ipa_traceframe_read_count_racy))
+ {
+ /* This will happen in most targets if the current thread is
+ running. */
+ return;
+ }
+
+ if (read_inferior_uinteger (ipa_sym_addrs.addr_traceframe_write_count,
+ &ipa_traceframe_write_count_racy))
+ return;
+
+ trace_debug ("ipa_traceframe_count (racy area): %d (w=%d, r=%d)",
+ ipa_traceframe_write_count_racy
+ - ipa_traceframe_read_count_racy,
+ ipa_traceframe_write_count_racy,
+ ipa_traceframe_read_count_racy);
+
+ if (ipa_traceframe_write_count_racy == ipa_traceframe_read_count_racy)
+ return;
+
+ about_to_request_buffer_space_bkpt
+ = set_breakpoint_at (ipa_sym_addrs.addr_about_to_request_buffer_space,
+ NULL);
+
+ if (read_inferior_uinteger (ipa_sym_addrs.addr_trace_buffer_ctrl_curr,
+ &ipa_trace_buffer_ctrl_curr))
+ return;
+
+ ipa_trace_buffer_ctrl_curr_old = ipa_trace_buffer_ctrl_curr;
+
+ curr_tbctrl_idx = ipa_trace_buffer_ctrl_curr & ~GDBSERVER_FLUSH_COUNT_MASK;
+
+ {
+ unsigned int prev, counter;
+
+ /* Update the token, with new counters, and the GDBserver stamp
+ bit. Alway reuse the current TBC index. */
+ prev = ipa_trace_buffer_ctrl_curr & GDBSERVER_FLUSH_COUNT_MASK_CURR;
+ counter = (prev + 0x100) & GDBSERVER_FLUSH_COUNT_MASK_CURR;
+
+ ipa_trace_buffer_ctrl_curr = (GDBSERVER_UPDATED_FLUSH_COUNT_BIT
+ | (prev << 12)
+ | counter
+ | curr_tbctrl_idx);
+ }
+
+ if (write_inferior_uinteger (ipa_sym_addrs.addr_trace_buffer_ctrl_curr,
+ ipa_trace_buffer_ctrl_curr))
+ return;
+
+ trace_debug ("Lib: Committed %08x -> %08x",
+ ipa_trace_buffer_ctrl_curr_old,
+ ipa_trace_buffer_ctrl_curr);
+
+ /* Re-read these, now that we've installed the
+ `about_to_request_buffer_space' breakpoint/lock. A thread could
+ have finished a traceframe between the last read of these
+ counters and setting the breakpoint above. If we start
+ uploading, we never want to leave this function with
+ traceframe_read_count != 0, otherwise, GDBserver could end up
+ incrementing the counter tokens more than once (due to event loop
+ nesting), which would break the IP agent's "effective" detection
+ (see trace_alloc_trace_buffer). */
+ if (read_inferior_uinteger (ipa_sym_addrs.addr_traceframe_read_count,
+ &ipa_traceframe_read_count))
+ return;
+ if (read_inferior_uinteger (ipa_sym_addrs.addr_traceframe_write_count,
+ &ipa_traceframe_write_count))
+ return;
+
+ if (debug_threads)
+ {
+ trace_debug ("ipa_traceframe_count (blocked area): %d (w=%d, r=%d)",
+ ipa_traceframe_write_count - ipa_traceframe_read_count,
+ ipa_traceframe_write_count, ipa_traceframe_read_count);
+
+ if (ipa_traceframe_write_count != ipa_traceframe_write_count_racy
+ || ipa_traceframe_read_count != ipa_traceframe_read_count_racy)
+ trace_debug ("note that ipa_traceframe_count's parts changed");
+ }
+
+ /* Get the address of the current TBC object (the IP agent has an
+ array of 3 such objects). The index is stored in the TBC
+ token. */
+ ipa_trace_buffer_ctrl_addr = ipa_sym_addrs.addr_trace_buffer_ctrl;
+ ipa_trace_buffer_ctrl_addr
+ += sizeof (struct ipa_trace_buffer_control) * curr_tbctrl_idx;
+
+ if (read_inferior_memory (ipa_trace_buffer_ctrl_addr,
+ (unsigned char *) &ipa_trace_buffer_ctrl,
+ sizeof (struct ipa_trace_buffer_control)))
+ return;
+
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_trace_buffer_lo,
+ &ipa_trace_buffer_lo))
+ return;
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_trace_buffer_hi,
+ &ipa_trace_buffer_hi))
+ return;
+
+ /* Offsets are easier to grok for debugging than raw addresses,
+ especially for the small trace buffer sizes that are useful for
+ testing. */
+ trace_debug ("Lib: Trace buffer [%d] start=%d free=%d "
+ "endfree=%d wrap=%d hi=%d",
+ curr_tbctrl_idx,
+ (int) (ipa_trace_buffer_ctrl.start - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.free - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.end_free - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.wrap - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_hi - ipa_trace_buffer_lo));
+
+ /* Note that the IPA's buffer is always circular. */
+
+#define IPA_FIRST_TRACEFRAME() (ipa_trace_buffer_ctrl.start)
+
+#define IPA_NEXT_TRACEFRAME_1(TF, TFOBJ) \
+ ((TF) + sizeof (struct traceframe) + (TFOBJ)->data_size)
+
+#define IPA_NEXT_TRACEFRAME(TF, TFOBJ) \
+ (IPA_NEXT_TRACEFRAME_1 (TF, TFOBJ) \
+ - ((IPA_NEXT_TRACEFRAME_1 (TF, TFOBJ) >= ipa_trace_buffer_ctrl.wrap) \
+ ? (ipa_trace_buffer_ctrl.wrap - ipa_trace_buffer_lo) \
+ : 0))
+
+ tf = IPA_FIRST_TRACEFRAME ();
+
+ while (ipa_traceframe_write_count - ipa_traceframe_read_count)
+ {
+ struct tracepoint *tpoint;
+ struct traceframe *tframe;
+ unsigned char *block;
+ struct traceframe ipa_tframe;
+
+ if (read_inferior_memory (tf, (unsigned char *) &ipa_tframe,
+ offsetof (struct traceframe, data)))
+ error ("Uploading: couldn't read traceframe at %s\n", paddress (tf));
+
+ if (ipa_tframe.tpnum == 0)
+ {
+ internal_error (__FILE__, __LINE__,
+ "Uploading: No (more) fast traceframes, but"
+ " ipa_traceframe_count == %u??\n",
+ ipa_traceframe_write_count
+ - ipa_traceframe_read_count);
+ }
+
+ /* Note that this will be incorrect for multi-location
+ tracepoints... */
+ tpoint = find_next_tracepoint_by_number (NULL, ipa_tframe.tpnum);
+
+ tframe = add_traceframe (tpoint);
+ if (tframe == NULL)
+ {
+ trace_buffer_is_full = 1;
+ trace_debug ("Uploading: trace buffer is full");
+ }
+ else
+ {
+ /* Copy the whole set of blocks in one go for now. FIXME:
+ split this in smaller blocks. */
+ block = add_traceframe_block (tframe, tpoint,
+ ipa_tframe.data_size);
+ if (block != NULL)
+ {
+ if (read_inferior_memory (tf
+ + offsetof (struct traceframe, data),
+ block, ipa_tframe.data_size))
+ error ("Uploading: Couldn't read traceframe data at %s\n",
+ paddress (tf + offsetof (struct traceframe, data)));
+ }
+
+ trace_debug ("Uploading: traceframe didn't fit");
+ finish_traceframe (tframe);
+ }
+
+ tf = IPA_NEXT_TRACEFRAME (tf, &ipa_tframe);
+
+ /* If we freed the traceframe that wrapped around, go back
+ to the non-wrap case. */
+ if (tf < ipa_trace_buffer_ctrl.start)
+ {
+ trace_debug ("Lib: Discarding past the wraparound");
+ ipa_trace_buffer_ctrl.wrap = ipa_trace_buffer_hi;
+ }
+ ipa_trace_buffer_ctrl.start = tf;
+ ipa_trace_buffer_ctrl.end_free = ipa_trace_buffer_ctrl.start;
+ ++ipa_traceframe_read_count;
+
+ if (ipa_trace_buffer_ctrl.start == ipa_trace_buffer_ctrl.free
+ && ipa_trace_buffer_ctrl.start == ipa_trace_buffer_ctrl.end_free)
+ {
+ trace_debug ("Lib: buffer is fully empty. "
+ "Trace buffer [%d] start=%d free=%d endfree=%d",
+ curr_tbctrl_idx,
+ (int) (ipa_trace_buffer_ctrl.start
+ - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.free
+ - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.end_free
+ - ipa_trace_buffer_lo));
+
+ ipa_trace_buffer_ctrl.start = ipa_trace_buffer_lo;
+ ipa_trace_buffer_ctrl.free = ipa_trace_buffer_lo;
+ ipa_trace_buffer_ctrl.end_free = ipa_trace_buffer_hi;
+ ipa_trace_buffer_ctrl.wrap = ipa_trace_buffer_hi;
+ }
+
+ trace_debug ("Uploaded a traceframe\n"
+ "Lib: Trace buffer [%d] start=%d free=%d "
+ "endfree=%d wrap=%d hi=%d",
+ curr_tbctrl_idx,
+ (int) (ipa_trace_buffer_ctrl.start - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.free - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.end_free
+ - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_ctrl.wrap - ipa_trace_buffer_lo),
+ (int) (ipa_trace_buffer_hi - ipa_trace_buffer_lo));
+ }
+
+ if (target_write_memory (ipa_trace_buffer_ctrl_addr,
+ (unsigned char *) &ipa_trace_buffer_ctrl,
+ sizeof (struct ipa_trace_buffer_control)))
+ return;
+
+ write_inferior_integer (ipa_sym_addrs.addr_traceframe_read_count,
+ ipa_traceframe_read_count);
+
+ trace_debug ("Done uploading traceframes [%d]\n", curr_tbctrl_idx);
+
+ pause_all (1);
+
+ delete_breakpoint (about_to_request_buffer_space_bkpt);
+ about_to_request_buffer_space_bkpt = NULL;
+
+ unpause_all (1);
+
+ if (trace_buffer_is_full)
+ stop_tracing ();
+}
+#endif
+
+#ifdef IN_PROCESS_AGENT
+
+IP_AGENT_EXPORT_VAR int ust_loaded;
+IP_AGENT_EXPORT_VAR char cmd_buf[IPA_CMD_BUF_SIZE];
+
+#ifdef HAVE_UST
+
+/* Static tracepoints. */
+
+/* UST puts a "struct tracepoint" in the global namespace, which
+ conflicts with our tracepoint. Arguably, being a library, it
+ shouldn't take ownership of such a generic name. We work around it
+ here. */
+#define tracepoint ust_tracepoint
+#include <ust/ust.h>
+#undef tracepoint
+
+extern int serialize_to_text (char *outbuf, int bufsize,
+ const char *fmt, va_list ap);
+
+#define GDB_PROBE_NAME "gdb"
+
+/* We dynamically search for the UST symbols instead of linking them
+ in. This lets the user decide if the application uses static
+ tracepoints, instead of always pulling libust.so in. This vector
+ holds pointers to all functions we care about. */
+
+static struct
+{
+ int (*serialize_to_text) (char *outbuf, int bufsize,
+ const char *fmt, va_list ap);
+
+ int (*ltt_probe_register) (struct ltt_available_probe *pdata);
+ int (*ltt_probe_unregister) (struct ltt_available_probe *pdata);
+
+ int (*ltt_marker_connect) (const char *channel, const char *mname,
+ const char *pname);
+ int (*ltt_marker_disconnect) (const char *channel, const char *mname,
+ const char *pname);
+
+ void (*marker_iter_start) (struct marker_iter *iter);
+ void (*marker_iter_next) (struct marker_iter *iter);
+ void (*marker_iter_stop) (struct marker_iter *iter);
+ void (*marker_iter_reset) (struct marker_iter *iter);
+} ust_ops;
+
+#include <dlfcn.h>
+
+/* Cast through typeof to catch incompatible API changes. Since UST
+ only builds with gcc, we can freely use gcc extensions here
+ too. */
+#define GET_UST_SYM(SYM) \
+ do \
+ { \
+ if (ust_ops.SYM == NULL) \
+ ust_ops.SYM = (typeof (&SYM)) dlsym (RTLD_DEFAULT, #SYM); \
+ if (ust_ops.SYM == NULL) \
+ return 0; \
+ } while (0)
+
+#define USTF(SYM) ust_ops.SYM
+
+/* Get pointers to all libust.so functions we care about. */
+
+static int
+dlsym_ust (void)
+{
+ GET_UST_SYM (serialize_to_text);
+
+ GET_UST_SYM (ltt_probe_register);
+ GET_UST_SYM (ltt_probe_unregister);
+ GET_UST_SYM (ltt_marker_connect);
+ GET_UST_SYM (ltt_marker_disconnect);
+
+ GET_UST_SYM (marker_iter_start);
+ GET_UST_SYM (marker_iter_next);
+ GET_UST_SYM (marker_iter_stop);
+ GET_UST_SYM (marker_iter_reset);
+
+ ust_loaded = 1;
+ return 1;
+}
+
+/* Given an UST marker, return the matching gdb static tracepoint.
+ The match is done by address. */
+
+static struct tracepoint *
+ust_marker_to_static_tracepoint (const struct marker *mdata)
+{
+ struct tracepoint *tpoint;
+
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ {
+ if (tpoint->type != static_tracepoint)
+ continue;
+
+ if (tpoint->address == (uintptr_t) mdata->location)
+ return tpoint;
+ }
+
+ return NULL;
+}
+
+/* The probe function we install on lttng/ust markers. Whenever a
+ probed ust marker is hit, this function is called. This is similar
+ to gdb_collect, only for static tracepoints, instead of fast
+ tracepoints. */
+
+static void
+gdb_probe (const struct marker *mdata, void *probe_private,
+ struct registers *regs, void *call_private,
+ const char *fmt, va_list *args)
+{
+ struct tracepoint *tpoint;
+ struct static_tracepoint_ctx ctx;
+ const struct target_desc *ipa_tdesc;
+
+ /* Don't do anything until the trace run is completely set up. */
+ if (!tracing)
+ {
+ trace_debug ("gdb_probe: not tracing\n");
+ return;
+ }
+
+ ipa_tdesc = get_ipa_tdesc (ipa_tdesc_idx);
+ ctx.base.type = static_tracepoint;
+ ctx.regcache_initted = 0;
+ ctx.regs = regs;
+ ctx.fmt = fmt;
+ ctx.args = args;
+
+ /* Wrap the regblock in a register cache (in the stack, we don't
+ want to malloc here). */
+ ctx.regspace = alloca (ipa_tdesc->registers_size);
+ if (ctx.regspace == NULL)
+ {
+ trace_debug ("Trace buffer block allocation failed, skipping");
+ return;
+ }
+
+ tpoint = ust_marker_to_static_tracepoint (mdata);
+ if (tpoint == NULL)
+ {
+ trace_debug ("gdb_probe: marker not known: "
+ "loc:0x%p, ch:\"%s\",n:\"%s\",f:\"%s\"",
+ mdata->location, mdata->channel,
+ mdata->name, mdata->format);
+ return;
+ }
+
+ if (!tpoint->enabled)
+ {
+ trace_debug ("gdb_probe: tracepoint disabled");
+ return;
+ }
+
+ ctx.tpoint = tpoint;
+
+ trace_debug ("gdb_probe: collecting marker: "
+ "loc:0x%p, ch:\"%s\",n:\"%s\",f:\"%s\"",
+ mdata->location, mdata->channel,
+ mdata->name, mdata->format);
+
+ /* Test the condition if present, and collect if true. */
+ if (tpoint->cond == NULL
+ || condition_true_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
+ tpoint))
+ {
+ collect_data_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
+ tpoint->address, tpoint);
+
+ 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 ();
+ }
+}
+
+/* Called if the gdb static tracepoint requested collecting "$_sdata",
+ static tracepoint string data. This is a string passed to the
+ tracing library by the user, at the time of the tracepoint marker
+ call. E.g., in the UST marker call:
+
+ trace_mark (ust, bar33, "str %s", "FOOBAZ");
+
+ the collected data is "str FOOBAZ".
+*/
+
+static void
+collect_ust_data_at_tracepoint (struct tracepoint_hit_ctx *ctx,
+ struct traceframe *tframe)
+{
+ struct static_tracepoint_ctx *umd = (struct static_tracepoint_ctx *) ctx;
+ unsigned char *bufspace;
+ int size;
+ va_list copy;
+ unsigned short blocklen;
+
+ if (umd == NULL)
+ {
+ trace_debug ("Wanted to collect static trace data, "
+ "but there's no static trace data");
+ return;
+ }
+
+ va_copy (copy, *umd->args);
+ size = USTF(serialize_to_text) (NULL, 0, umd->fmt, copy);
+ va_end (copy);
+
+ trace_debug ("Want to collect ust data");
+
+ /* 'S' + size + string */
+ bufspace = add_traceframe_block (tframe, umd->tpoint,
+ 1 + sizeof (blocklen) + size + 1);
+ if (bufspace == NULL)
+ {
+ trace_debug ("Trace buffer block allocation failed, skipping");
+ return;
+ }
+
+ /* Identify a static trace data block. */
+ *bufspace = 'S';
+
+ blocklen = size + 1;
+ memcpy (bufspace + 1, &blocklen, sizeof (blocklen));
+
+ va_copy (copy, *umd->args);
+ USTF(serialize_to_text) ((char *) bufspace + 1 + sizeof (blocklen),
+ size + 1, umd->fmt, copy);
+ va_end (copy);
+
+ trace_debug ("Storing static tracepoint data in regblock: %s",
+ bufspace + 1 + sizeof (blocklen));
+}
+
+/* The probe to register with lttng/ust. */
+static struct ltt_available_probe gdb_ust_probe =
+ {
+ GDB_PROBE_NAME,
+ NULL,
+ gdb_probe,
+ };
+
+#endif /* HAVE_UST */
+#endif /* IN_PROCESS_AGENT */
+
+#ifndef IN_PROCESS_AGENT
+
+/* 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
+ the helper thread re-running. We communicate with the helper
+ thread by means of direct memory xfering, and a socket for
+ synchronization. */
+
+static int
+run_inferior_command (char *cmd, int len)
+{
+ int err = -1;
+ int pid = current_ptid.pid ();
+
+ trace_debug ("run_inferior_command: running: %s", cmd);
+
+ pause_all (0);
+ uninsert_all_breakpoints ();
+
+ err = agent_run_command (pid, (const char *) cmd, len);
+
+ reinsert_all_breakpoints ();
+ unpause_all (0);
+
+ return err;
+}
+
+#else /* !IN_PROCESS_AGENT */
+
+#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
+
+/* Thread ID of the helper thread. GDBserver reads this to know which
+ is the help thread. This is an LWP id on Linux. */
+EXTERN_C_PUSH
+IP_AGENT_EXPORT_VAR int helper_thread_id;
+EXTERN_C_POP
+
+static int
+init_named_socket (const char *name)
+{
+ int result, fd;
+ struct sockaddr_un addr;
+
+ result = fd = socket (PF_UNIX, SOCK_STREAM, 0);
+ if (result == -1)
+ {
+ warning ("socket creation failed: %s", safe_strerror (errno));
+ return -1;
+ }
+
+ addr.sun_family = AF_UNIX;
+
+ strncpy (addr.sun_path, name, UNIX_PATH_MAX);
+ addr.sun_path[UNIX_PATH_MAX - 1] = '\0';
+
+ result = access (name, F_OK);
+ if (result == 0)
+ {
+ /* File exists. */
+ result = unlink (name);
+ if (result == -1)
+ {
+ warning ("unlink failed: %s", safe_strerror (errno));
+ close (fd);
+ return -1;
+ }
+ warning ("socket %s already exists; overwriting", name);
+ }
+
+ result = bind (fd, (struct sockaddr *) &addr, sizeof (addr));
+ if (result == -1)
+ {
+ warning ("bind failed: %s", safe_strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ result = listen (fd, 1);
+ if (result == -1)
+ {
+ warning ("listen: %s", safe_strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static char agent_socket_name[UNIX_PATH_MAX];
+
+static int
+gdb_agent_socket_init (void)
+{
+ int result, fd;
+
+ result = xsnprintf (agent_socket_name, UNIX_PATH_MAX, "%s/gdb_ust%d",
+ SOCK_DIR, getpid ());
+ if (result >= UNIX_PATH_MAX)
+ {
+ trace_debug ("string overflow allocating socket name");
+ return -1;
+ }
+
+ 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.", agent_socket_name);
+
+ return fd;
+}
+
+#ifdef HAVE_UST
+
+/* The next marker to be returned on a qTsSTM command. */
+static const struct marker *next_st;
+
+/* Returns the first known marker. */
+
+struct marker *
+first_marker (void)
+{
+ struct marker_iter iter;
+
+ USTF(marker_iter_reset) (&iter);
+ USTF(marker_iter_start) (&iter);
+
+ return iter.marker;
+}
+
+/* Returns the marker following M. */
+
+const struct marker *
+next_marker (const struct marker *m)
+{
+ struct marker_iter iter;
+
+ USTF(marker_iter_reset) (&iter);
+ USTF(marker_iter_start) (&iter);
+
+ for (; iter.marker != NULL; USTF(marker_iter_next) (&iter))
+ {
+ if (iter.marker == m)
+ {
+ USTF(marker_iter_next) (&iter);
+ return iter.marker;
+ }
+ }
+
+ 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);
+ bin2hex ((gdb_byte *) str, hexstr, len);
+ return hexstr;
+}
+
+/* Compose packet that is the response to the qTsSTM/qTfSTM/qTSTMat
+ packets. */
+
+static void
+response_ust_marker (char *packet, const struct marker *st)
+{
+ char *strid, *format, *tmp;
+
+ next_st = next_marker (st);
+
+ tmp = xmalloc (strlen (st->channel) + 1 +
+ strlen (st->name) + 1);
+ sprintf (tmp, "%s/%s", st->channel, st->name);
+
+ strid = cstr_to_hexstr (tmp);
+ free (tmp);
+
+ format = cstr_to_hexstr (st->format);
+
+ sprintf (packet, "m%s:%s:%s",
+ paddress ((uintptr_t) st->location),
+ strid,
+ format);
+
+ free (strid);
+ free (format);
+}
+
+/* Return the first static tracepoint, and initialize the state
+ machine that will iterate through all the static tracepoints. */
+
+static void
+cmd_qtfstm (char *packet)
+{
+ trace_debug ("Returning first trace state variable definition");
+
+ if (first_marker ())
+ response_ust_marker (packet, first_marker ());
+ else
+ strcpy (packet, "l");
+}
+
+/* Return additional trace state variable definitions. */
+
+static void
+cmd_qtsstm (char *packet)
+{
+ trace_debug ("Returning static tracepoint");
+
+ if (next_st)
+ response_ust_marker (packet, next_st);
+ else
+ strcpy (packet, "l");
+}
+
+/* Disconnect the GDB probe from a marker at a given address. */
+
+static void
+unprobe_marker_at (char *packet)
+{
+ char *p = packet;
+ ULONGEST address;
+ struct marker_iter iter;
+
+ p += sizeof ("unprobe_marker_at:") - 1;
+
+ p = unpack_varlen_hex (p, &address);
+
+ USTF(marker_iter_reset) (&iter);
+ USTF(marker_iter_start) (&iter);
+ for (; iter.marker != NULL; USTF(marker_iter_next) (&iter))
+ if ((uintptr_t ) iter.marker->location == address)
+ {
+ int result;
+
+ result = USTF(ltt_marker_disconnect) (iter.marker->channel,
+ iter.marker->name,
+ GDB_PROBE_NAME);
+ if (result < 0)
+ warning ("could not disable marker %s/%s",
+ iter.marker->channel, iter.marker->name);
+ break;
+ }
+}
+
+/* Connect the GDB probe to a marker at a given address. */
+
+static int
+probe_marker_at (char *packet)
+{
+ char *p = packet;
+ ULONGEST address;
+ struct marker_iter iter;
+ struct marker *m;
+
+ p += sizeof ("probe_marker_at:") - 1;
+
+ p = unpack_varlen_hex (p, &address);
+
+ USTF(marker_iter_reset) (&iter);
+
+ for (USTF(marker_iter_start) (&iter), m = iter.marker;
+ m != NULL;
+ USTF(marker_iter_next) (&iter), m = iter.marker)
+ if ((uintptr_t ) m->location == address)
+ {
+ int result;
+
+ trace_debug ("found marker for address. "
+ "ltt_marker_connect (marker = %s/%s)",
+ m->channel, m->name);
+
+ result = USTF(ltt_marker_connect) (m->channel, m->name,
+ GDB_PROBE_NAME);
+ if (result && result != -EEXIST)
+ trace_debug ("ltt_marker_connect (marker = %s/%s, errno = %d)",
+ m->channel, m->name, -result);
+
+ if (result < 0)
+ {
+ sprintf (packet, "E.could not connect marker: channel=%s, name=%s",
+ m->channel, m->name);
+ return -1;
+ }
+
+ strcpy (packet, "OK");
+ return 0;
+ }
+
+ sprintf (packet, "E.no marker found at 0x%s", paddress (address));
+ return -1;
+}
+
+static int
+cmd_qtstmat (char *packet)
+{
+ char *p = packet;
+ ULONGEST address;
+ struct marker_iter iter;
+ struct marker *m;
+
+ p += sizeof ("qTSTMat:") - 1;
+
+ p = unpack_varlen_hex (p, &address);
+
+ USTF(marker_iter_reset) (&iter);
+
+ for (USTF(marker_iter_start) (&iter), m = iter.marker;
+ m != NULL;
+ USTF(marker_iter_next) (&iter), m = iter.marker)
+ if ((uintptr_t ) m->location == address)
+ {
+ response_ust_marker (packet, m);
+ return 0;
+ }
+
+ strcpy (packet, "l");
+ 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>
+
+static void
+gdb_agent_remove_socket (void)
+{
+ unlink (agent_socket_name);
+}
+
+/* Helper thread of agent. */
+
+static void *
+gdb_agent_helper_thread (void *arg)
+{
+ int listen_fd;
+
+ atexit (gdb_agent_remove_socket);
+
+ while (1)
+ {
+ listen_fd = gdb_agent_socket_init ();
+
+ if (helper_thread_id == 0)
+ helper_thread_id = syscall (SYS_gettid);
+
+ if (listen_fd == -1)
+ {
+ warning ("could not create sync socket");
+ break;
+ }
+
+ while (1)
+ {
+ socklen_t tmp;
+ struct sockaddr_un sockaddr;
+ int fd;
+ char buf[1];
+ int ret;
+ int stop_loop = 0;
+
+ tmp = sizeof (sockaddr);
+
+ do
+ {
+ fd = accept (listen_fd, (struct sockaddr *) &sockaddr, &tmp);
+ }
+ /* It seems an ERESTARTSYS can escape out of accept. */
+ while (fd == -512 || (fd == -1 && errno == EINTR));
+
+ if (fd < 0)
+ {
+ warning ("Accept returned %d, error: %s",
+ fd, safe_strerror (errno));
+ break;
+ }
+
+ do
+ {
+ ret = read (fd, buf, 1);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == -1)
+ {
+ warning ("reading socket (fd=%d) failed with %s",
+ fd, safe_strerror (errno));
+ close (fd);
+ break;
+ }
+
+ if (cmd_buf[0])
+ {
+ if (startswith (cmd_buf, "close"))
+ {
+ stop_loop = 1;
+ }
+#ifdef HAVE_UST
+ else if (strcmp ("qTfSTM", cmd_buf) == 0)
+ {
+ cmd_qtfstm (cmd_buf);
+ }
+ else if (strcmp ("qTsSTM", cmd_buf) == 0)
+ {
+ cmd_qtsstm (cmd_buf);
+ }
+ else if (startswith (cmd_buf, "unprobe_marker_at:"))
+ {
+ unprobe_marker_at (cmd_buf);
+ }
+ else if (startswith (cmd_buf, "probe_marker_at:"))
+ {
+ probe_marker_at (cmd_buf);
+ }
+ else if (startswith (cmd_buf, "qTSTMat:"))
+ {
+ cmd_qtstmat (cmd_buf);
+ }
+#endif /* HAVE_UST */
+ }
+
+ /* 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_thread' (representing this thread) to access
+ inferior memory. Otherwise, this thread exits earlier than
+ other threads, and 'current_thread' is set to NULL. */
+ while (1)
+ sleep (10);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+#include <signal.h>
+#include <pthread.h>
+
+EXTERN_C_PUSH
+IP_AGENT_EXPORT_VAR int gdb_agent_capability = AGENT_CAPA_STATIC_TRACE;
+EXTERN_C_POP
+
+static void
+gdb_agent_init (void)
+{
+ int res;
+ pthread_t thread;
+ sigset_t new_mask;
+ sigset_t orig_mask;
+
+ /* We want the helper thread to be as transparent as possible, so
+ have it inherit an all-signals-blocked mask. */
+
+ sigfillset (&new_mask);
+ res = pthread_sigmask (SIG_SETMASK, &new_mask, &orig_mask);
+ if (res)
+ perror_with_name ("pthread_sigmask (1)");
+
+ res = pthread_create (&thread,
+ NULL,
+ gdb_agent_helper_thread,
+ NULL);
+
+ res = pthread_sigmask (SIG_SETMASK, &orig_mask, NULL);
+ if (res)
+ perror_with_name ("pthread_sigmask (2)");
+
+ while (helper_thread_id == 0)
+ usleep (1);
+
+#ifdef HAVE_UST
+ gdb_ust_init ();
+#endif
+}
+
+#include <sys/mman.h>
+
+IP_AGENT_EXPORT_VAR char *gdb_tp_heap_buffer;
+IP_AGENT_EXPORT_VAR char *gdb_jump_pad_buffer;
+IP_AGENT_EXPORT_VAR char *gdb_jump_pad_buffer_end;
+IP_AGENT_EXPORT_VAR char *gdb_trampoline_buffer;
+IP_AGENT_EXPORT_VAR char *gdb_trampoline_buffer_end;
+IP_AGENT_EXPORT_VAR 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 ();
+
+ gdb_agent_init ();
+}
+
+#ifndef HAVE_GETAUXVAL
+/* Retrieve the value of TYPE from the auxiliary vector. If TYPE is not
+ found, 0 is returned. This function is provided if glibc is too old. */
+
+unsigned long
+getauxval (unsigned long type)
+{
+ unsigned long data[2];
+ FILE *f = fopen ("/proc/self/auxv", "r");
+ unsigned long value = 0;
+
+ if (f == NULL)
+ return 0;
+
+ while (fread (data, sizeof (data), 1, f) > 0)
+ {
+ if (data[0] == type)
+ {
+ value = data[1];
+ break;
+ }
+ }
+
+ fclose (f);
+ return value;
+}
+#endif
+
+#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
+get_timestamp (void)
+{
+ using namespace std::chrono;
+
+ steady_clock::time_point now = steady_clock::now ();
+ return duration_cast<microseconds> (now.time_since_epoch ()).count ();
+}
+
+void
+initialize_tracepoint (void)
+{
+ /* Start with the default size. */
+ init_trace_buffer (DEFAULT_TRACE_BUFFER_SIZE);
+
+ /* Wire trace state variable 1 to be the timestamp. This will be
+ uploaded to GDB upon connection and become one of its trace state
+ variables. (In case you're wondering, if GDB already has a trace
+ 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, get_timestamp);
+
+#ifdef IN_PROCESS_AGENT
+ {
+ int pagesize;
+ size_t jump_pad_size;
+
+ pagesize = sysconf (_SC_PAGE_SIZE);
+ if (pagesize == -1)
+ perror_with_name ("sysconf");
+
+#define SCRATCH_BUFFER_NPAGES 20
+
+ jump_pad_size = pagesize * SCRATCH_BUFFER_NPAGES;
+
+ gdb_tp_heap_buffer = (char *) xmalloc (5 * 1024 * 1024);
+ gdb_jump_pad_buffer = (char *) alloc_jump_pad_buffer (jump_pad_size);
+ if (gdb_jump_pad_buffer == NULL)
+ perror_with_name ("mmap");
+ gdb_jump_pad_buffer_end = gdb_jump_pad_buffer + jump_pad_size;
+ }
+
+ 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 = (char *) xmalloc (IPA_BUFSIZ);
+
+ strcpy (gdb_trampoline_buffer_error, "No errors reported");
+
+ initialize_low_tracepoint ();
+#endif
+}