|  | /* Tracepoint code for remote server for GDB. | 
|  | Copyright (C) 2009-2022 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(fmt, args...)	\ | 
|  | do {						\ | 
|  | if (debug_threads)				\ | 
|  | trace_vdebug ((fmt), ##args);		\ | 
|  | } while (0) | 
|  |  | 
|  | #else | 
|  |  | 
|  | #define trace_debug(fmt, args...)	\ | 
|  | do {						\ | 
|  | threads_debug_printf ((fmt), ##args);	\ | 
|  | } while (0) | 
|  |  | 
|  | #endif | 
|  |  | 
|  | /* 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) | 
|  | { | 
|  | threads_debug_printf ("symbol `%s' not found", 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.  */ | 
|  | static struct breakpoint *stop_tracing_bkpt; | 
|  | static int stop_tracing_handler (CORE_ADDR); | 
|  |  | 
|  | /* Breakpoint at "flush_trace_buffer" in the inferior lib.  */ | 
|  | static 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 * const 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 | 
|  | static 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.  */ | 
|  | static 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.  */ | 
|  |  | 
|  | static LONGEST tracing_start_time; | 
|  | static 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.  */ | 
|  |  | 
|  | static char *tracing_user_name; | 
|  |  | 
|  | /* Optional user-supplied text describing the run.  This is | 
|  | an arbitrary string, and may be NULL.  */ | 
|  |  | 
|  | static char *tracing_notes; | 
|  |  | 
|  | /* Optional user-supplied text explaining a tstop command.  This is an | 
|  | arbitrary string, and may be NULL.  */ | 
|  |  | 
|  | static 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; | 
|  |  | 
|  | target_pause_all (true); | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | target_unpause_all (true); | 
|  | } | 
|  |  | 
|  | /* 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.  */ | 
|  | target_pause_all (false); | 
|  |  | 
|  | /* download_tracepoint will update global `tracepoints' | 
|  | list, so it is unsafe to leave threads in jump pad.  */ | 
|  | target_stabilize_threads (); | 
|  |  | 
|  | /* Freeze threads.  */ | 
|  | target_pause_all (true); | 
|  |  | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | target_unpause_all (true); | 
|  | 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 offset = offsetof (struct tracepoint, enabled); | 
|  | CORE_ADDR obj_addr = tp->obj_addr_on_target + offset; | 
|  |  | 
|  | int ret = write_inferior_int8 (obj_addr, enable); | 
|  | 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 = target_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.  */ | 
|  | target_pause_all (false); | 
|  |  | 
|  | /* 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.  */ | 
|  |  | 
|  | target_stabilize_threads (); | 
|  |  | 
|  | /* Freeze threads.  */ | 
|  | target_pause_all (true); | 
|  |  | 
|  | /* 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); | 
|  | target_unpause_all (true); | 
|  | 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"); | 
|  | } | 
|  |  | 
|  | target_unpause_all (true); | 
|  |  | 
|  | 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.  */ | 
|  | target_pause_all (true); | 
|  |  | 
|  | /* 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; | 
|  | } | 
|  |  | 
|  | target_unpause_all (true); | 
|  | } | 
|  |  | 
|  | 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 ()) | 
|  | { | 
|  | target_pause_all (true); | 
|  |  | 
|  | upload_fast_traceframes (); | 
|  |  | 
|  | target_unpause_all (true); | 
|  | } | 
|  |  | 
|  | 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)) | 
|  | { | 
|  | scoped_restore_current_thread restore_thread; | 
|  |  | 
|  | /* Find any thread which belongs to process PID.  */ | 
|  | switch_to_thread (find_any_thread_of_pid (pid)); | 
|  |  | 
|  | strcpy (buf, "close"); | 
|  |  | 
|  | run_inferior_command (buf, strlen (buf) + 1); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* 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).c_str (), | 
|  | 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).c_str ()); | 
|  |  | 
|  | /* 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).c_str (), | 
|  | 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 (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).c_str (), | 
|  | 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); | 
|  |  | 
|  | target_pause_all (true); | 
|  |  | 
|  | delete_breakpoint (about_to_request_buffer_space_bkpt); | 
|  | about_to_request_buffer_space_bkpt = NULL; | 
|  |  | 
|  | target_unpause_all (true); | 
|  |  | 
|  | 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); | 
|  |  | 
|  | target_pause_all (false); | 
|  | uninsert_all_breakpoints (); | 
|  |  | 
|  | err = agent_run_command (pid, (const char *) cmd, len); | 
|  |  | 
|  | reinsert_all_breakpoints (); | 
|  | target_unpause_all (false); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | if (strlen (name) >= ARRAY_SIZE (addr.sun_path)) | 
|  | { | 
|  | warning ("socket name too long for sockaddr_un::sun_path field: %s", name); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | strcpy (addr.sun_path, name); | 
|  |  | 
|  | 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 = snprintf (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 | 
|  | } |