diff options
author | Simon Marchi <simon.marchi@efficios.com> | 2020-02-13 16:27:51 -0500 |
---|---|---|
committer | Simon Marchi <simon.marchi@efficios.com> | 2020-02-13 16:27:51 -0500 |
commit | feacfcacaac9f7e62f467a33c4ae54c56501ed18 (patch) | |
tree | 0e083366350e9fd279bec8c7b12413c90b9f255d /gdbserver/tracepoint.cc | |
parent | 06b3c5bdb018262d7c09cda9d4637015c7ebe779 (diff) | |
download | gdb-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.cc | 7473 |
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 (®cache, tdesc, dataptr); + return regcache_read_pc (®cache); +} + +/* 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 +} |