| /* Tracepoint code for remote server for GDB. |
| Copyright (C) 2009, 2010 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 <ctype.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <sys/time.h> |
| |
| static void trace_debug_1 (const char *, ...) ATTR_FORMAT (printf, 1, 2); |
| |
| static void |
| trace_debug_1 (const char *fmt, ...) |
| { |
| char buf[1024]; |
| va_list ap; |
| |
| va_start (ap, fmt); |
| vsprintf (buf, fmt, ap); |
| fprintf (stderr, "gdbserver/tracepoint: %s\n", buf); |
| va_end (ap); |
| } |
| |
| #define trace_debug(FMT, args...) \ |
| do { \ |
| if (debug_threads) \ |
| trace_debug_1 ((FMT), ##args); \ |
| } while (0) |
| |
| static int |
| tracepoint_handler (CORE_ADDR address) |
| { |
| trace_debug ("tracepoint_handler: tracepoint at 0x%s hit", |
| paddress (address)); |
| return 0; |
| } |
| |
| /* This enum must exactly match what is documented in |
| gdb/doc/agentexpr.texi, including all the numerical values. */ |
| |
| enum gdb_agent_op |
| { |
| gdb_agent_op_float = 0x01, |
| gdb_agent_op_add = 0x02, |
| gdb_agent_op_sub = 0x03, |
| gdb_agent_op_mul = 0x04, |
| gdb_agent_op_div_signed = 0x05, |
| gdb_agent_op_div_unsigned = 0x06, |
| gdb_agent_op_rem_signed = 0x07, |
| gdb_agent_op_rem_unsigned = 0x08, |
| gdb_agent_op_lsh = 0x09, |
| gdb_agent_op_rsh_signed = 0x0a, |
| gdb_agent_op_rsh_unsigned = 0x0b, |
| gdb_agent_op_trace = 0x0c, |
| gdb_agent_op_trace_quick = 0x0d, |
| gdb_agent_op_log_not = 0x0e, |
| gdb_agent_op_bit_and = 0x0f, |
| gdb_agent_op_bit_or = 0x10, |
| gdb_agent_op_bit_xor = 0x11, |
| gdb_agent_op_bit_not = 0x12, |
| gdb_agent_op_equal = 0x13, |
| gdb_agent_op_less_signed = 0x14, |
| gdb_agent_op_less_unsigned = 0x15, |
| gdb_agent_op_ext = 0x16, |
| gdb_agent_op_ref8 = 0x17, |
| gdb_agent_op_ref16 = 0x18, |
| gdb_agent_op_ref32 = 0x19, |
| gdb_agent_op_ref64 = 0x1a, |
| gdb_agent_op_ref_float = 0x1b, |
| gdb_agent_op_ref_double = 0x1c, |
| gdb_agent_op_ref_long_double = 0x1d, |
| gdb_agent_op_l_to_d = 0x1e, |
| gdb_agent_op_d_to_l = 0x1f, |
| gdb_agent_op_if_goto = 0x20, |
| gdb_agent_op_goto = 0x21, |
| gdb_agent_op_const8 = 0x22, |
| gdb_agent_op_const16 = 0x23, |
| gdb_agent_op_const32 = 0x24, |
| gdb_agent_op_const64 = 0x25, |
| gdb_agent_op_reg = 0x26, |
| gdb_agent_op_end = 0x27, |
| gdb_agent_op_dup = 0x28, |
| gdb_agent_op_pop = 0x29, |
| gdb_agent_op_zero_ext = 0x2a, |
| gdb_agent_op_swap = 0x2b, |
| gdb_agent_op_getv = 0x2c, |
| gdb_agent_op_setv = 0x2d, |
| gdb_agent_op_tracev = 0x2e, |
| gdb_agent_op_trace16 = 0x30, |
| gdb_agent_op_last |
| }; |
| |
| static const char *gdb_agent_op_names [gdb_agent_op_last] = |
| { |
| "?undef?", |
| "float", |
| "add", |
| "sub", |
| "mul", |
| "div_signed", |
| "div_unsigned", |
| "rem_signed", |
| "rem_unsigned", |
| "lsh", |
| "rsh_signed", |
| "rsh_unsigned", |
| "trace", |
| "trace_quick", |
| "log_not", |
| "bit_and", |
| "bit_or", |
| "bit_xor", |
| "bit_not", |
| "equal", |
| "less_signed", |
| "less_unsigned", |
| "ext", |
| "ref8", |
| "ref16", |
| "ref32", |
| "ref64", |
| "ref_float", |
| "ref_double", |
| "ref_long_double", |
| "l_to_d", |
| "d_to_l", |
| "if_goto", |
| "goto", |
| "const8", |
| "const16", |
| "const32", |
| "const64", |
| "reg", |
| "end", |
| "dup", |
| "pop", |
| "zero_ext", |
| "swap", |
| "getv", |
| "setv", |
| "tracev", |
| "?undef?", |
| "trace16", |
| }; |
| |
| struct agent_expr |
| { |
| int length; |
| |
| unsigned char *bytes; |
| }; |
| |
| /* 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; |
| int 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; |
| }; |
| |
| /* 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; |
| }; |
| |
| struct tracepoint_hit_ctx; |
| |
| /* 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. */ |
| int number; |
| |
| /* Address at which the tracepoint is supposed to trigger. Several |
| tracepoints may share an address. */ |
| CORE_ADDR address; |
| |
| /* True if the tracepoint is currently enabled. */ |
| int enabled; |
| |
| /* The number of single steps that will be performed after each |
| tracepoint hit. */ |
| long step_count; |
| |
| /* The number of times the tracepoint may be hit before it will |
| terminate the entire tracing run. */ |
| long pass_count; |
| |
| /* Pointer to the agent expression that is the tracepoint's |
| conditional, or NULL if the tracepoint is unconditional. */ |
| struct agent_expr *cond; |
| |
| /* The list of actions to take when the tracepoint triggers. */ |
| int numactions; |
| struct tracepoint_action **actions; |
| /* Same, but in string/packet form. */ |
| char **actions_str; |
| |
| /* The list of actions to take while in a stepping loop. */ |
| int num_step_actions; |
| struct tracepoint_action **step_actions; |
| /* Same, but in string/packet form. */ |
| char **step_actions_str; |
| |
| /* Count of the times we've hit this tracepoint during the run. |
| Note that while-stepping steps are not counted as "hits". */ |
| long hit_count; |
| |
| /* 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; |
| |
| /* Handle returned by the breakpoint module when we inserted the |
| trap. NULL if we haven't inserted it yet. */ |
| void *handle; |
| |
| /* Link to the next tracepoint in the list. */ |
| struct tracepoint *next; |
| }; |
| |
| /* 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; |
| }; |
| |
| /* The linked list of all tracepoints. */ |
| |
| static struct tracepoint *tracepoints; |
| |
| /* Pointer to the last tracepoint in the list, new tracepoints are |
| linked in at the end. */ |
| |
| static struct tracepoint *last_tracepoint; |
| |
| /* The first tracepoint to exceed its pass count. */ |
| |
| static struct tracepoint *stopping_tracepoint; |
| |
| /* True if the trace buffer is full or otherwise no longer usable. */ |
| |
| static int trace_buffer_is_full; |
| |
| /* Enumeration of the different kinds of things that can happen during |
| agent expression evaluation. */ |
| |
| enum eval_result_type |
| { |
| expr_eval_no_error, |
| expr_eval_empty_expression, |
| expr_eval_empty_stack, |
| expr_eval_stack_overflow, |
| expr_eval_stack_underflow, |
| expr_eval_unhandled_opcode, |
| expr_eval_unrecognized_opcode, |
| expr_eval_divide_by_zero, |
| expr_eval_invalid_goto |
| }; |
| |
| static enum eval_result_type expr_eval_result = expr_eval_no_error; |
| |
| static const char *eval_result_names[] = |
| { |
| "terror:in the attic", /* this should never be reported */ |
| "terror:empty expression", |
| "terror:empty stack", |
| "terror:stack overflow", |
| "terror:stack underflow", |
| "terror:unhandled opcode", |
| "terror:unrecognized opcode", |
| "terror:divide by zero" |
| }; |
| |
| /* The tracepoint in which the error occurred. */ |
| |
| static struct tracepoint *error_tracepoint; |
| |
| 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. */ |
| |
| static 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 traceframe_t; |
| |
| /* The traceframe to be used as the source of data to send back to |
| GDB. A value of -1 means to get data from the live program. */ |
| |
| int current_traceframe = -1; |
| |
| /* 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. */ |
| |
| static int circular_trace_buffer; |
| |
| /* Pointer to the block of memory that traceframes all go into. */ |
| |
| static unsigned char *trace_buffer_lo; |
| |
| /* Pointer to the end of the trace buffer, more precisely to the byte |
| after the end of the buffer. */ |
| |
| static unsigned char *trace_buffer_hi; |
| |
| /* 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. */ |
| |
| static unsigned char *trace_buffer_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. */ |
| |
| static unsigned char *trace_buffer_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. */ |
| |
| static unsigned char *trace_buffer_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. */ |
| |
| static unsigned char *trace_buffer_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. */ |
| |
| static unsigned int traceframe_write_count; |
| static 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. */ |
| |
| static int traceframes_created; |
| |
| /* 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; |
| |
| /* The global that controls tracing overall. */ |
| |
| int tracing; |
| |
| /* 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; |
| |
| /* 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 |
| { |
| /* empty */ |
| }; |
| |
| /* Trap tracepoint specific data to be passed down to |
| collect_data_at_tracepoint. */ |
| |
| struct trap_tracepoint_ctx |
| { |
| struct tracepoint_hit_ctx base; |
| |
| struct regcache *regcache; |
| }; |
| |
| static struct agent_expr *parse_agent_expr (char **actparm); |
| static char *unparse_agent_expr (struct agent_expr *aexpr); |
| static enum eval_result_type eval_agent_expr (struct tracepoint_hit_ctx *ctx, |
| struct traceframe *tframe, |
| struct agent_expr *aexpr, |
| ULONGEST *rslt); |
| |
| static int agent_mem_read (struct traceframe *tframe, |
| unsigned char *to, CORE_ADDR from, ULONGEST len); |
| static int agent_tsv_read (struct traceframe *tframe, int n); |
| |
| static CORE_ADDR traceframe_get_pc (struct traceframe *tframe); |
| static int traceframe_read_tsv (int num, LONGEST *val); |
| |
| static int condition_true_at_tracepoint (struct tracepoint_hit_ctx *ctx, |
| struct tracepoint *tpoint); |
| |
| static void clear_readonly_regions (void); |
| static void clear_installed_tracepoints (void); |
| |
| static void collect_data_at_tracepoint (struct tracepoint_hit_ctx *ctx, |
| CORE_ADDR stop_pc, |
| struct tracepoint *tpoint); |
| |
| static void collect_data_at_step (struct tracepoint_hit_ctx *ctx, |
| CORE_ADDR stop_pc, |
| struct tracepoint *tpoint, int current_step); |
| |
| 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); |
| |
| /* 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); |
| |
| expr_eval_result = rtype; |
| 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; |
| } |
| |
| static void |
| init_trace_buffer (unsigned char *buf, int bufsize) |
| { |
| trace_buffer_lo = buf; |
| trace_buffer_hi = trace_buffer_lo + bufsize; |
| |
| clear_trace_buffer (); |
| } |
| |
| /* 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 traceframe *oldest; |
| unsigned char *new_start; |
| |
| trace_debug ("Want to allocate %ld+%ld bytes in trace buffer", |
| (long) amt, (long) sizeof (struct traceframe)); |
| |
| /* Account for the EOB marker. */ |
| amt += sizeof (struct traceframe); |
| |
| /* 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 start=%d free=%d endfree=%d wrap=%d hi=%d", |
| (int) (trace_buffer_start - trace_buffer_lo), |
| (int) (trace_buffer_free - trace_buffer_lo), |
| (int) (trace_buffer_end_free - trace_buffer_lo), |
| (int) (trace_buffer_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 (trace_buffer_end_free < trace_buffer_free) |
| { |
| if (trace_buffer_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"); |
| trace_buffer_wrap = trace_buffer_free; |
| trace_buffer_free = trace_buffer_lo; |
| } |
| } |
| |
| /* The normal case. */ |
| if (trace_buffer_free + amt <= trace_buffer_end_free) |
| break; |
| |
| /* 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; |
| } |
| |
| --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 < trace_buffer_start) |
| { |
| trace_debug ("Discarding past the wraparound"); |
| trace_buffer_wrap = trace_buffer_hi; |
| } |
| trace_buffer_start = new_start; |
| trace_buffer_end_free = trace_buffer_start; |
| |
| trace_debug ("Discarded a traceframe\n" |
| "Trace buffer, start=%d free=%d endfree=%d wrap=%d hi=%d", |
| (int) (trace_buffer_start - trace_buffer_lo), |
| (int) (trace_buffer_free - trace_buffer_lo), |
| (int) (trace_buffer_end_free - trace_buffer_lo), |
| (int) (trace_buffer_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. */ |
| } |
| |
| /* If we get here, we know we can provide the asked-for space. */ |
| |
| rslt = trace_buffer_free; |
| |
| /* Adjust the request back down, now that we know we have space for |
| the marker. */ |
| trace_buffer_free += (amt - sizeof (struct traceframe)); |
| |
| /* We have a new piece of the trace buffer. Hurray! */ |
| |
| /* Add an EOB marker just past this allocation. */ |
| ((struct traceframe *) trace_buffer_free)->tpnum = 0; |
| ((struct traceframe *) trace_buffer_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 start=%d free=%d endfree=%d wrap=%d hi=%d", |
| (int) (trace_buffer_start - trace_buffer_lo), |
| (int) (trace_buffer_free - trace_buffer_lo), |
| (int) (trace_buffer_end_free - trace_buffer_lo), |
| (int) (trace_buffer_wrap - trace_buffer_lo), |
| (int) (trace_buffer_hi - trace_buffer_lo)); |
| } |
| |
| return rslt; |
| } |
| |
| /* 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. */ |
| |
| static struct tracepoint * |
| add_tracepoint (int num, CORE_ADDR addr) |
| { |
| struct tracepoint *tpoint; |
| |
| tpoint = xmalloc (sizeof (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; |
| tpoint->source_strings = NULL; |
| tpoint->handle = NULL; |
| tpoint->next = NULL; |
| |
| if (!last_tracepoint) |
| tracepoints = tpoint; |
| else |
| last_tracepoint->next = tpoint; |
| last_tracepoint = tpoint; |
| |
| seen_step_action_flag = 0; |
| |
| return tpoint; |
| } |
| |
| /* 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; |
| } |
| |
| /* 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; |
| } |
| |
| static char * |
| save_string (const char *str, size_t len) |
| { |
| char *s; |
| |
| s = xmalloc (len + 1); |
| memcpy (s, str, len); |
| s[len] = '\0'; |
| |
| return s; |
| } |
| |
| /* Append another action to perform when the tracepoint triggers. */ |
| |
| static void |
| add_tracepoint_action (struct tracepoint *tpoint, char *packet) |
| { |
| char *act; |
| |
| if (*packet == 'S') |
| { |
| seen_step_action_flag = 1; |
| ++packet; |
| } |
| |
| act = packet; |
| |
| while (*act) |
| { |
| char *act_start = act; |
| struct tracepoint_action *action = NULL; |
| |
| switch (*act) |
| { |
| case 'M': |
| { |
| struct collect_memory_action *maction; |
| ULONGEST basereg; |
| int is_neg; |
| |
| maction = xmalloc (sizeof *maction); |
| 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; |
| |
| raction = xmalloc (sizeof *raction); |
| 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 'S': |
| trace_debug ("Unexpected step action, ignoring"); |
| ++act; |
| break; |
| case 'X': |
| { |
| struct eval_expr_action *xaction; |
| |
| xaction = xmalloc (sizeof (*xaction)); |
| xaction->base.type = *act; |
| action = &xaction->base; |
| |
| trace_debug ("Want to evaluate expression"); |
| xaction->expr = 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 |
| = xrealloc (tpoint->step_actions, |
| (sizeof (*tpoint->step_actions) |
| * tpoint->num_step_actions)); |
| tpoint->step_actions_str |
| = xrealloc (tpoint->step_actions_str, |
| (sizeof (*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] |
| = save_string (act_start, act - act_start); |
| } |
| else |
| { |
| tpoint->numactions++; |
| tpoint->actions |
| = xrealloc (tpoint->actions, |
| sizeof (*tpoint->actions) * tpoint->numactions); |
| tpoint->actions_str |
| = xrealloc (tpoint->actions_str, |
| sizeof (*tpoint->actions_str) * tpoint->numactions); |
| tpoint->actions[tpoint->numactions - 1] = action; |
| tpoint->actions_str[tpoint->numactions - 1] |
| = save_string (act_start, act - act_start); |
| } |
| } |
| } |
| |
| /* 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; |
| |
| /* 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) |
| { |
| struct trace_state_variable *tsv; |
| |
| tsv = get_trace_state_variable (num); |
| if (tsv != NULL) |
| return tsv; |
| |
| /* Create a new variable. */ |
| tsv = xmalloc (sizeof (struct trace_state_variable)); |
| tsv->number = num; |
| tsv->initial_value = 0; |
| tsv->value = 0; |
| tsv->getter = NULL; |
| tsv->name = NULL; |
| tsv->next = trace_state_variables; |
| trace_state_variables = tsv; |
| |
| return tsv; |
| } |
| |
| static 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; |
| } |
| |
| static 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; |
| } |
| |
| 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 = 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, int amt) |
| { |
| unsigned char *block; |
| |
| if (!tframe) |
| return NULL; |
| |
| block = trace_buffer_alloc (amt); |
| |
| if (!block) |
| return NULL; |
| |
| tframe->data_size += amt; |
| |
| return block; |
| } |
| |
| /* Flag that the current traceframe is finished. */ |
| |
| static void |
| finish_traceframe (struct traceframe *tframe) |
| { |
| ++traceframe_write_count; |
| ++traceframes_created; |
| } |
| |
| /* 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) |
| { |
| struct traceframe *tframe; |
| CORE_ADDR tfaddr; |
| |
| *tfnump = 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) |
| { |
| struct traceframe *tframe; |
| |
| *tfnump = 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; |
| } |
| |
| /* Clear all past trace state. */ |
| |
| static void |
| cmd_qtinit (char *packet) |
| { |
| struct trace_state_variable *tsv, *prev, *next; |
| |
| /* Make sure we don't try to read from a trace frame. */ |
| current_traceframe = -1; |
| |
| 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 (); |
| |
| write_ok (packet); |
| } |
| |
| /* 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; |
| |
| prev_stpoint = NULL; |
| |
| /* Restore any bytes overwritten by tracepoints. */ |
| for (tpoint = tracepoints; tpoint; tpoint = tpoint->next) |
| { |
| if (!tpoint->enabled) |
| continue; |
| |
| /* Catch the case where we might try to remove a tracepoint that |
| was never actually installed. */ |
| if (tpoint->handle == NULL) |
| { |
| trace_debug ("Tracepoint %d at 0x%s was " |
| "never installed, nothing to clear", |
| tpoint->number, paddress (tpoint->address)); |
| continue; |
| } |
| |
| delete_breakpoint (tpoint->handle); |
| tpoint->handle = NULL; |
| } |
| } |
| |
| /* Parse a packet that defines a tracepoint. */ |
| |
| static void |
| cmd_qtdp (char *own_buf) |
| { |
| int tppacket; |
| ULONGEST num; |
| ULONGEST addr; |
| ULONGEST count; |
| struct tracepoint *tpoint; |
| char *actparm; |
| 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 == 'X') |
| { |
| actparm = (char *) packet; |
| tpoint->cond = parse_agent_expr (&actparm); |
| packet = actparm; |
| } |
| else if (*packet == '-') |
| break; |
| else if (*packet == '\0') |
| break; |
| else |
| trace_debug ("Unknown optional tracepoint field"); |
| } |
| if (*packet == '-') |
| trace_debug ("Also has actions\n"); |
| |
| trace_debug ("Defined tracepoint %d at 0x%s, " |
| "enabled %d step %ld pass %ld", |
| 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; |
| } |
| |
| write_ok (own_buf); |
| } |
| |
| static void |
| cmd_qtdpsrc (char *own_buf) |
| { |
| ULONGEST num, addr, start, slen; |
| struct tracepoint *tpoint; |
| char *packet = own_buf; |
| char *saved, *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 = 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 = xmalloc (slen + 1); |
| nbytes = unhexify (src, packet, strlen (packet) / 2); |
| src[nbytes] = '\0'; |
| |
| newlast = xmalloc (sizeof (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; |
| 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 = xmalloc (nbytes + 1); |
| nbytes = unhexify (varname, packet, nbytes); |
| varname[nbytes] = '\0'; |
| |
| tsv = create_trace_state_variable (num); |
| tsv->initial_value = (LONGEST) val; |
| tsv->name = varname; |
| |
| set_trace_state_variable_value (num, (LONGEST) val); |
| |
| write_ok (own_buf); |
| } |
| |
| static void |
| cmd_qtv (char *own_buf) |
| { |
| ULONGEST num; |
| LONGEST val; |
| int err; |
| char *packet = own_buf; |
| |
| packet += strlen ("qTV:"); |
| packet = unpack_varlen_hex (packet, &num); |
| |
| if (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; |
| 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 = xmalloc (sizeof (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 void |
| cmd_qtstart (char *packet) |
| { |
| struct tracepoint *tpoint; |
| int slow_tracepoint_count; |
| |
| trace_debug ("Starting the trace"); |
| |
| slow_tracepoint_count = 0; |
| |
| *packet = '\0'; |
| |
| /* Install tracepoints. */ |
| for (tpoint = tracepoints; tpoint; tpoint = tpoint->next) |
| { |
| /* Ensure all the hit counts start at zero. */ |
| tpoint->hit_count = 0; |
| |
| if (!tpoint->enabled) |
| continue; |
| |
| ++slow_tracepoint_count; |
| |
| /* Tracepoints are installed as memory breakpoints. Just go |
| ahead and install the trap. The breakpoints module handles |
| duplicated breakpoints, and the memory read routine handles |
| un-patching traps from memory reads. */ |
| tpoint->handle = set_breakpoint_at (tpoint->address, tracepoint_handler); |
| |
| /* Any failure 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); |
| return; |
| } |
| |
| stopping_tracepoint = NULL; |
| trace_buffer_is_full = 0; |
| expr_eval_result = expr_eval_no_error; |
| error_tracepoint = NULL; |
| |
| /* Tracing is now active, hits will now start being logged. */ |
| tracing = 1; |
| |
| write_ok (packet); |
| } |
| |
| /* End a tracing run, filling in a stop reason to report back to GDB, |
| and removing the tracepoints from the code. */ |
| |
| void |
| stop_tracing (void) |
| { |
| if (!tracing) |
| { |
| trace_debug ("Tracing is already off, ignoring"); |
| return; |
| } |
| |
| trace_debug ("Stopping the trace"); |
| |
| /* Stop logging. Tracepoints can still be hit, but they will not be |
| recorded. */ |
| tracing = 0; |
| |
| tracing_stop_reason = "t???"; |
| tracing_stop_tpnum = 0; |
| if (stopping_tracepoint) |
| { |
| trace_debug ("Stopping the trace because " |
| "tracepoint %d was hit %ld 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; |
| } |
| else if (!gdb_connected ()) |
| { |
| trace_debug ("Stopping the trace because GDB disconnected"); |
| tracing_stop_reason = "tdisconnected"; |
| } |
| 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 (); |
| } |
| |
| 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) |
| { |
| ULONGEST frame, pc, lo, hi, num; |
| int tfnum, tpnum; |
| struct traceframe *tframe; |
| char *packet = own_buf; |
| |
| packet += strlen ("QTFrame:"); |
| |
| if (strncmp (packet, "pc:", strlen ("pc:")) == 0) |
| { |
| packet += strlen ("pc:"); |
| packet = 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 (strncmp (packet, "range:", strlen ("range:")) == 0) |
| { |
| packet += strlen ("range:"); |
| packet = unpack_varlen_hex (packet, &lo); |
| ++packet; |
| 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 (strncmp (packet, "outside:", strlen ("outside:")) == 0) |
| { |
| packet += strlen ("outside:"); |
| packet = unpack_varlen_hex (packet, &lo); |
| ++packet; |
| 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 (strncmp (packet, "tdp:", strlen ("tdp:")) == 0) |
| { |
| packet += strlen ("tdp:"); |
| packet = 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"); |
| current_traceframe = -1; |
| write_ok (own_buf); |
| return; |
| } |
| trace_debug ("Want to look at traceframe %d", tfnum); |
| tframe = find_traceframe (tfnum); |
| } |
| |
| if (tframe) |
| { |
| 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; |
| |
| trace_debug ("Returning trace status as %d, stop reason %s", |
| tracing, tracing_stop_reason); |
| |
| stop_reason_rsp = (char *) tracing_stop_reason; |
| |
| /* The user visible error string in terror needs to be hex encoded. |
| We leave it as plain string in `tracepoint_stop_reason' to ease |
| debugging. */ |
| if (strncmp (stop_reason_rsp, "terror:", strlen ("terror:")) == 0) |
| { |
| 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 = alloca (strlen ("terror:") + hexstr_len + 1); |
| strcpy (p, "terror:"); |
| p += strlen (p); |
| convert_int_to_ascii ((gdb_byte *) result_name, p, strlen (result_name)); |
| } |
| |
| sprintf (packet, |
| "T%d;" |
| "%s:%x;" |
| "tframes:%x;tcreated:%x;" |
| "tfree:%x;tsize:%s;" |
| "circular:%d;" |
| "disconn:%d", |
| 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); |
| } |
| |
| /* State variables to help return all the tracepoint bits. */ |
| static struct tracepoint *cur_tpoint; |
| static int cur_action; |
| static 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:%lx:%lx", tpoint->number, |
| paddress (tpoint->address), |
| (tpoint->enabled ? 'E' : 'D'), tpoint->step_count, |
| tpoint->pass_count); |
| |
| if (tpoint->cond) |
| { |
| buf = 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 = alloca (len * 2 + 1); |
| convert_int_to_ascii ((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 = -1; |
| 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 - 1) |
| { |
| ++cur_action; |
| response_action (packet, cur_tpoint, |
| cur_tpoint->actions_str[cur_action], 0); |
| } |
| else if (cur_step_action < cur_tpoint->num_step_actions - 1) |
| { |
| ++cur_step_action; |
| response_action (packet, cur_tpoint, |
| cur_tpoint->step_actions_str[cur_step_action], 1); |
| } |
| 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 = -1; |
| 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 = alloca (namelen * 2 + 1); |
| convert_int_to_ascii ((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 first trace state variable definition"); |
| |
| if (!cur_tpoint) |
| { |
| /* This case would normally never occur, but be prepared for |
| GDB misbehavior. */ |
| strcpy (packet, "l"); |
| } |
| else if (cur_tsv) |
| { |
| cur_tsv = cur_tsv->next; |
| if (cur_tsv) |
| response_tsv (packet, cur_tsv); |
| else |
| strcpy (packet, "l"); |
| } |
| else |
| strcpy (packet, "l"); |
| } |
| |
| /* 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; |
| char *packet = own_buf; |
| |
| packet += strlen ("qTBuffer:"); |
| |
| packet = unpack_varlen_hex (packet, &offset); |
| ++packet; /* skip a comma */ |
| packet = unpack_varlen_hex (packet, &num); |
| |
| trace_debug ("Want to get trace buffer, %d bytes at offset 0x%s", |
| (int) num, pulongest (offset)); |
| |
| 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; |
| |
| convert_int_to_ascii (tbp, own_buf, num); |
| own_buf[num] = '\0'; |
| } |
| |
| static void |
| cmd_bigqtbuffer (char *own_buf) |
| { |
| ULONGEST val; |
| char *packet = own_buf; |
| |
| packet += strlen ("QTBuffer:"); |
| |
| if (strncmp ("circular:", packet, strlen ("circular:")) == 0) |
| { |
| packet += strlen ("circular:"); |
| packet = unpack_varlen_hex (packet, &val); |
| circular_trace_buffer = val; |
| trace_debug ("Trace buffer is now %s", |
| circular_trace_buffer ? "circular" : "linear"); |
| write_ok (own_buf); |
| } |
| else |
| write_enn (own_buf); |
| } |
| |
| int |
| handle_tracepoint_general_set (char *packet) |
| { |
| if (strcmp ("QTinit", packet) == 0) |
| { |
| cmd_qtinit (packet); |
| return 1; |
| } |
| else if (strncmp ("QTDP:", packet, strlen ("QTDP:")) == 0) |
| { |
| cmd_qtdp (packet); |
| return 1; |
| } |
| else if (strncmp ("QTDPsrc:", packet, strlen ("QTDPsrc:")) == 0) |
| { |
| cmd_qtdpsrc (packet); |
| return 1; |
| } |
| else if (strncmp ("QTDV:", packet, strlen ("QTDV:")) == 0) |
| { |
| cmd_qtdv (packet); |
| return 1; |
| } |
| else if (strncmp ("QTro:", packet, strlen ("QTro:")) == 0) |
| { |
| 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 (strncmp ("QTDisconnected:", packet, |
| strlen ("QTDisconnected:")) == 0) |
| { |
| cmd_qtdisconnected (packet); |
| return 1; |
| } |
| else if (strncmp ("QTFrame:", packet, strlen ("QTFrame:")) == 0) |
| { |
| cmd_qtframe (packet); |
| return 1; |
| } |
| else if (strncmp ("QTBuffer:", packet, strlen ("QTBuffer:")) == 0) |
| { |
| cmd_bigqtbuffer (packet); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| int |
| handle_tracepoint_query (char *packet) |
| { |
| if (strcmp ("qTStatus", packet) == 0) |
| { |
| cmd_qtstatus (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 (strncmp ("qTV:", packet, strlen ("qTV:")) == 0) |
| { |
| cmd_qtv (packet); |
| return 1; |
| } |
| else if (strncmp ("qTBuffer:", packet, strlen ("qTBuffer:")) == 0) |
| { |
| cmd_qtbuffer (packet); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* 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; |
| |
| wstep = xmalloc (sizeof (*wstep)); |
| 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; |
| |
| /* 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->entry.id), |
| wstep->tp_number, paddress (wstep->tp_address)); |
| |
| 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->entry.id)); |
| |
| /* Unlink. */ |
| *wstep_link = wstep->next; |
| release_while_stepping_state (wstep); |
| 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->entry.id), |
| wstep->tp_number, paddress (wstep->tp_address)); |
| |
| /* Unlink the wstep. */ |
| *wstep_link = wstep->next; |
| release_while_stepping_state (wstep); |
| wstep = *wstep_link; |
| |
| /* Only check the hit count now, which ensure that we do all |
| our stepping before stopping the run. */ |
| if (tpoint->pass_count > 0 |
| && tpoint->hit_count >= tpoint->pass_count |
| && stopping_tracepoint == NULL) |
| stopping_tracepoint = tpoint; |
| } |
| else |
| { |
| /* Keep single-stepping until the requested numbers of steps |
| have occurred. */ |
| wstep_link = &wstep->next; |
| wstep = *wstep_link; |
| } |
| |
| if (stopping_tracepoint |
| || trace_buffer_is_full |
| || expr_eval_result != expr_eval_no_error) |
| { |
| stop_tracing (); |
| break; |
| } |
| } |
| |
| return 1; |
| } |
| |
| /* 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.regcache = get_thread_regcache (tinfo, 1); |
| |
| for (tpoint = tracepoints; tpoint; tpoint = tpoint->next) |
| { |
| if (tpoint->enabled && stop_pc == tpoint->address) |
| { |
| trace_debug ("Thread %s at address of tracepoint %d at 0x%s", |
| target_pid_to_str (tinfo->entry.id), |
| tpoint->number, paddress (tpoint->address)); |
| |
| /* Test the condition if present, and collect if true. */ |
| if (!tpoint->cond |
| || (condition_true_at_tracepoint |
| ((struct tracepoint_hit_ctx *) &ctx, tpoint))) |
| collect_data_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx, |
| stop_pc, tpoint); |
| |
| if (stopping_tracepoint |
| || trace_buffer_is_full |
| || expr_eval_result != expr_eval_no_error) |
| { |
| stop_tracing (); |
| } |
| /* If the tracepoint had a 'while-stepping' action, then set |
| the thread to collect this tracepoint on the following |
| single-steps. */ |
| else if (tpoint->step_count > 0) |
| { |
| add_while_stepping_state (tinfo, |
| tpoint->number, tpoint->address); |
| } |
| |
| ret = 1; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /* 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 %ld", |
| tpoint->number, paddress (tpoint->address), tpoint->hit_count); |
| |
| tframe = add_traceframe (tpoint); |
| |
| if (tframe) |
| { |
| for (acti = 0; acti < tpoint->numactions; ++acti) |
| { |
| trace_debug ("Tracepoint %d at 0x%s about to do action '%s'", |
| tpoint->number, paddress (tpoint->address), |
| tpoint->actions_str[acti]); |
| |
| do_action_at_tracepoint (ctx, stop_pc, tpoint, tframe, |
| tpoint->actions[acti]); |
| } |
| |
| finish_traceframe (tframe); |
| } |
| |
| if (tframe == NULL && tracing) |
| trace_buffer_is_full = 1; |
| } |
| |
| 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 %ld, hit %ld", |
| 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; |
| } |
| |
| static struct regcache * |
| get_context_regcache (struct tracepoint_hit_ctx *ctx) |
| { |
| struct trap_tracepoint_ctx *tctx = (struct trap_tracepoint_ctx *) ctx; |
| struct regcache *regcache = tctx->regcache; |
| |
| 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; |
| |
| maction = (struct collect_memory_action *) taction; |
| |
| trace_debug ("Want to collect %s bytes at 0x%s (basereg %d)", |
| pulongest (maction->len), |
| paddress (maction->addr), maction->basereg); |
| /* (should use basereg) */ |
| agent_mem_read (tframe, NULL, |
| (CORE_ADDR) maction->addr, maction->len); |
| break; |
| } |
| case 'R': |
| { |
| struct collect_registers_action *raction; |
| |
| unsigned char *regspace; |
| struct regcache tregcache; |
| struct regcache *context_regcache; |
| |
| raction = (struct collect_registers_action *) taction; |
| |
| trace_debug ("Want to collect registers"); |
| |
| /* Collect all registers for now. */ |
| regspace = add_traceframe_block (tframe, |
| 1 + register_cache_size ()); |
| if (regspace == NULL) |
| { |
| trace_debug ("Trace buffer block allocation failed, skipping"); |
| break; |
| } |
| /* Identify a register block. */ |
| *regspace = 'R'; |
| |
| context_regcache = get_context_regcache (ctx); |
| |
| /* Wrap the regblock in a register cache (in the stack, we |
| don't want to malloc here). */ |
| init_register_cache (&tregcache, regspace + 1); |
| |
| /* Copy the register data to the regblock. */ |
| regcache_cpy (&tregcache, context_regcache); |
| |
| /* 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. */ |
| trace_debug ("Storing stop pc (0x%s) in regblock", |
| paddress (tpoint->address)); |
| |
| /* This changes the regblock, not the thread's |
| regcache. */ |
| regcache_write_pc (&tregcache, stop_pc); |
| } |
| break; |
| case 'X': |
| { |
| struct eval_expr_action *eaction; |
| |
| eaction = (struct eval_expr_action *) taction; |
| |
| trace_debug ("Want to evaluate expression"); |
| |
| err = eval_agent_expr (ctx, tframe, eaction->expr, NULL); |
| |
| if (err != expr_eval_no_error) |
| { |
| record_tracepoint_error (tpoint, "action expression", err); |
| return; |
| } |
| } |
| 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; |
| |
| err = eval_agent_expr (ctx, NULL, 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); |
| } |
| |
| /* The packet form of an agent expression consists of an 'X', number |
| of bytes in expression, a comma, and then the bytes. */ |
| |
| static struct agent_expr * |
| parse_agent_expr (char **actparm) |
| { |
| char *act = *actparm; |
| ULONGEST xlen; |
| struct agent_expr *aexpr; |
| |
| ++act; /* skip the X */ |
| act = unpack_varlen_hex (act, &xlen); |
| ++act; /* skip a comma */ |
| aexpr = xmalloc (sizeof (struct agent_expr)); |
| aexpr->length = xlen; |
| aexpr->bytes = xmalloc (xlen); |
| convert_ascii_to_int (act, aexpr->bytes, xlen); |
| *actparm = act + (xlen * 2); |
| return aexpr; |
| } |
| |
| /* Convert the bytes of an agent expression back into hex digits, so |
| they can be printed or uploaded. This allocates the buffer, |
| callers should free when they are done with it. */ |
| |
| static char * |
| unparse_agent_expr (struct agent_expr *aexpr) |
| { |
| char *rslt; |
| |
| rslt = xmalloc (2 * aexpr->length + 1); |
| convert_int_to_ascii (aexpr->bytes, rslt, aexpr->length); |
| return rslt; |
| } |
| |
| /* The agent expression evaluator, as specified by the GDB docs. It |
| returns 0 if everything went OK, and a nonzero error code |
| otherwise. */ |
| |
| static enum eval_result_type |
| eval_agent_expr (struct tracepoint_hit_ctx *ctx, |
| struct traceframe *tframe, |
| struct agent_expr *aexpr, |
| ULONGEST *rslt) |
| { |
| int pc = 0; |
| #define STACK_MAX 100 |
| ULONGEST stack[STACK_MAX], top; |
| int sp = 0; |
| unsigned char op; |
| int arg; |
| |
| /* This union is a convenient way to convert representations. For |
| now, assume a standard architecture where the hardware integer |
| types have 8, 16, 32, 64 bit types. A more robust solution would |
| be to import stdint.h from gnulib. */ |
| union |
| { |
| union |
| { |
| unsigned char bytes[1]; |
| unsigned char val; |
| } u8; |
| union |
| { |
| unsigned char bytes[2]; |
| unsigned short val; |
| } u16; |
| union |
| { |
| unsigned char bytes[4]; |
| unsigned int val; |
| } u32; |
| union |
| { |
| unsigned char bytes[8]; |
| ULONGEST val; |
| } u64; |
| } cnv; |
| |
| if (aexpr->length == 0) |
| { |
| trace_debug ("empty agent expression"); |
| return expr_eval_empty_expression; |
| } |
| |
| /* Cache the stack top in its own variable. Much of the time we can |
| operate on this variable, rather than dinking with the stack. It |
| needs to be copied to the stack when sp changes. */ |
| top = 0; |
| |
| while (1) |
| { |
| op = aexpr->bytes[pc++]; |
| |
| trace_debug ("About to interpret byte 0x%x", op); |
| |
| switch (op) |
| { |
| case gdb_agent_op_add: |
| top += stack[--sp]; |
| break; |
| |
| case gdb_agent_op_sub: |
| top = stack[--sp] - top; |
| break; |
| |
| case gdb_agent_op_mul: |
| top *= stack[--sp]; |
| break; |
| |
| case gdb_agent_op_div_signed: |
| if (top == 0) |
| { |
| trace_debug ("Attempted to divide by zero"); |
| return expr_eval_divide_by_zero; |
| } |
| top = ((LONGEST) stack[--sp]) / ((LONGEST) top); |
| break; |
| |
| case gdb_agent_op_div_unsigned: |
| if (top == 0) |
| { |
| trace_debug ("Attempted to divide by zero"); |
| return expr_eval_divide_by_zero; |
| } |
| top = stack[--sp] / top; |
| break; |
| |
| case gdb_agent_op_rem_signed: |
| if (top == 0) |
| { |
| trace_debug ("Attempted to divide by zero"); |
| return expr_eval_divide_by_zero; |
| } |
| top = ((LONGEST) stack[--sp]) % ((LONGEST) top); |
| break; |
| |
| case gdb_agent_op_rem_unsigned: |
| if (top == 0) |
| { |
| trace_debug ("Attempted to divide by zero"); |
| return expr_eval_divide_by_zero; |
| } |
| top = stack[--sp] % top; |
| break; |
| |
| case gdb_agent_op_lsh: |
| top = stack[--sp] << top; |
| break; |
| |
| case gdb_agent_op_rsh_signed: |
| top = ((LONGEST) stack[--sp]) >> top; |
| break; |
| |
| case gdb_agent_op_rsh_unsigned: |
| top = stack[--sp] >> top; |
| break; |
| |
| case gdb_agent_op_trace: |
| agent_mem_read (tframe, |
| NULL, (CORE_ADDR) stack[--sp], (ULONGEST) top); |
| if (--sp >= 0) |
| top = stack[sp]; |
| break; |
| |
| case gdb_agent_op_trace_quick: |
| arg = aexpr->bytes[pc++]; |
| agent_mem_read (tframe, NULL, (CORE_ADDR) top, (ULONGEST) arg); |
| break; |
| |
| case gdb_agent_op_log_not: |
| top = !top; |
| break; |
| |
| case gdb_agent_op_bit_and: |
| top &= stack[--sp]; |
| break; |
| |
| case gdb_agent_op_bit_or: |
| top |= stack[--sp]; |
| break; |
| |
| case gdb_agent_op_bit_xor: |
| top ^= stack[--sp]; |
| break; |
| |
| case gdb_agent_op_bit_not: |
| top = ~top; |
| break; |
| |
| case gdb_agent_op_equal: |
| top = (stack[--sp] == top); |
| break; |
| |
| case gdb_agent_op_less_signed: |
| top = (((LONGEST) stack[--sp]) < ((LONGEST) top)); |
| break; |
| |
| case gdb_agent_op_less_unsigned: |
| top = (stack[--sp] < top); |
| break; |
| |
| case gdb_agent_op_ext: |
| arg = aexpr->bytes[pc++]; |
| if (arg < (sizeof (LONGEST) * 8)) |
| { |
| LONGEST mask = 1 << (arg - 1); |
| top &= ((LONGEST) 1 << arg) - 1; |
| top = (top ^ mask) - mask; |
| } |
| break; |
| |
| case gdb_agent_op_ref8: |
| agent_mem_read (tframe, cnv.u8.bytes, (CORE_ADDR) top, 1); |
| top = cnv.u8.val; |
| break; |
| |
| case gdb_agent_op_ref16: |
| agent_mem_read (tframe, cnv.u16.bytes, (CORE_ADDR) top, 2); |
| top = cnv.u16.val; |
| break; |
| |
| case gdb_agent_op_ref32: |
| agent_mem_read (tframe, cnv.u32.bytes, (CORE_ADDR) top, 4); |
| top = cnv.u32.val; |
| break; |
| |
| case gdb_agent_op_ref64: |
| agent_mem_read (tframe, cnv.u64.bytes, (CORE_ADDR) top, 8); |
| top = cnv.u64.val; |
| break; |
| |
| case gdb_agent_op_if_goto: |
| if (top) |
| pc = (aexpr->bytes[pc] << 8) + (aexpr->bytes[pc + 1]); |
| else |
| pc += 2; |
| if (--sp >= 0) |
| top = stack[sp]; |
| break; |
| |
| case gdb_agent_op_goto: |
| pc = (aexpr->bytes[pc] << 8) + (aexpr->bytes[pc + 1]); |
| break; |
| |
| case gdb_agent_op_const8: |
| /* Flush the cached stack top. */ |
| stack[sp++] = top; |
| top = aexpr->bytes[pc++]; |
| break; |
| |
| case gdb_agent_op_const16: |
| /* Flush the cached stack top. */ |
| stack[sp++] = top; |
| top = aexpr->bytes[pc++]; |
| top = (top << 8) + aexpr->bytes[pc++]; |
| break; |
| |
| case gdb_agent_op_const32: |
| /* Flush the cached stack top. */ |
| stack[sp++] = top; |
| top = aexpr->bytes[pc++]; |
| top = (top << 8) + aexpr->bytes[pc++]; |
| top = (top << 8) + aexpr->bytes[pc++]; |
| top = (top << 8) + aexpr->bytes[pc++]; |
| break; |
| |
| case gdb_agent_op_const64: |
| /* Flush the cached stack top. */ |
| stack[sp++] = top; |
| top = aexpr->bytes[pc++]; |
| top = (top << 8) + aexpr->bytes[pc++]; |
| top = (top << 8) + aexpr->bytes[pc++]; |
| top = (top << 8) + aexpr->bytes[pc++]; |
| top = (top << 8) + aexpr->bytes[pc++]; |
| top = (top << 8) + aexpr->bytes[pc++]; |
| top = (top << 8) + aexpr->bytes[pc++]; |
| top = (top << 8) + aexpr->bytes[pc++]; |
| break; |
| |
| case gdb_agent_op_reg: |
| /* Flush the cached stack top. */ |
| stack[sp++] = top; |
| arg = aexpr->bytes[pc++]; |
| arg = (arg << 8) + aexpr->bytes[pc++]; |
| { |
| int regnum = arg; |
| struct regcache *regcache; |
| |
| regcache = get_context_regcache (ctx); |
| |
| switch (register_size (regnum)) |
| { |
| case 8: |
| collect_register (regcache, regnum, cnv.u64.bytes); |
| top = cnv.u64.val; |
| break; |
| case 4: |
| collect_register (regcache, regnum, cnv.u32.bytes); |
| top = cnv.u32.val; |
| break; |
| case 2: |
| collect_register (regcache, regnum, cnv.u16.bytes); |
| top = cnv.u16.val; |
| break; |
| case 1: |
| collect_register (regcache, regnum, cnv.u8.bytes); |
| top = cnv.u8.val; |
| break; |
| default: |
| internal_error (__FILE__, __LINE__, |
| "unhandled register size"); |
| } |
| } |
| break; |
| |
| case gdb_agent_op_end: |
| trace_debug ("At end of expression, sp=%d, stack top cache=0x%s", |
| sp, pulongest (top)); |
| if (rslt) |
| { |
| if (sp <= 0) |
| { |
| /* This should be an error */ |
| trace_debug ("Stack is empty, nothing to return"); |
| return expr_eval_empty_stack; |
| } |
| *rslt = top; |
| } |
| return expr_eval_no_error; |
| |
| case gdb_agent_op_dup: |
| stack[sp++] = top; |
| break; |
| |
| case gdb_agent_op_pop: |
| if (--sp >= 0) |
| top = stack[sp]; |
| break; |
| |
| case gdb_agent_op_zero_ext: |
| arg = aexpr->bytes[pc++]; |
| if (arg < (sizeof (LONGEST) * 8)) |
| top &= ((LONGEST) 1 << arg) - 1; |
| break; |
| |
| case gdb_agent_op_swap: |
| /* Interchange top two stack elements, making sure top gets |
| copied back onto stack. */ |
| stack[sp] = top; |
| top = stack[sp - 1]; |
| stack[sp - 1] = stack[sp]; |
| break; |
| |
| case gdb_agent_op_getv: |
| /* Flush the cached stack top. */ |
| stack[sp++] = top; |
| arg = aexpr->bytes[pc++]; |
| arg = (arg << 8) + aexpr->bytes[pc++]; |
| top = get_trace_state_variable_value (arg); |
| break; |
| |
| case gdb_agent_op_setv: |
| arg = aexpr->bytes[pc++]; |
| arg = (arg << 8) + aexpr->bytes[pc++]; |
| set_trace_state_variable_value (arg, top); |
| /* Note that we leave the value on the stack, for the |
| benefit of later/enclosing expressions. */ |
| break; |
| |
| case gdb_agent_op_tracev: |
| arg = aexpr->bytes[pc++]; |
| arg = (arg << 8) + aexpr->bytes[pc++]; |
| agent_tsv_read (tframe, arg); |
| break; |
| |
| /* GDB never (currently) generates any of these ops. */ |
| case gdb_agent_op_float: |
| case gdb_agent_op_ref_float: |
| case gdb_agent_op_ref_double: |
| case gdb_agent_op_ref_long_double: |
| case gdb_agent_op_l_to_d: |
| case gdb_agent_op_d_to_l: |
| case gdb_agent_op_trace16: |
| trace_debug ("Agent expression op 0x%x valid, but not handled", |
| op); |
| /* If ever GDB generates any of these, we don't have the |
| option of ignoring. */ |
| return 1; |
| |
| default: |
| trace_debug ("Agent expression op 0x%x not recognized", op); |
| /* Don't struggle on, things will just get worse. */ |
| return expr_eval_unrecognized_opcode; |
| } |
| |
| /* Check for stack badness. */ |
| if (sp >= (STACK_MAX - 1)) |
| { |
| trace_debug ("Expression stack overflow"); |
| return expr_eval_stack_overflow; |
| } |
| |
| if (sp < 0) |
| { |
| trace_debug ("Expression stack underflow"); |
| return expr_eval_stack_underflow; |
| } |
| |
| trace_debug ("Op %s -> sp=%d, top=0x%s", |
| gdb_agent_op_names[op], sp, pulongest (top)); |
| } |
| } |
| |
| /* Do memory copies for bytecodes. */ |
| /* Do the recording of memory blocks for actions and bytecodes. */ |
| |
| static int |
| agent_mem_read (struct traceframe *tframe, |
| unsigned char *to, CORE_ADDR from, ULONGEST len) |
| { |
| unsigned char *mspace; |
| ULONGEST remaining = len; |
| unsigned short blocklen; |
| |
| /* If a 'to' buffer is specified, use it. */ |
| if (to != NULL) |
| { |
| 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 (tframe, 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; |
| } |
| |
| /* Record the value of a trace state variable. */ |
| |
| static int |
| agent_tsv_read (struct traceframe *tframe, int n) |
| { |
| unsigned char *vspace; |
| LONGEST val; |
| |
| vspace = add_traceframe_block (tframe, |
| 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; |
| } |
| |
| static unsigned char * |
| traceframe_find_block_type (unsigned char *database, unsigned int datasize, |
| int tfnum, char type_wanted) |
| { |
| 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 (type_wanted == blocktype) |
| return dataptr; |
| |
| switch (blocktype) |
| { |
| case 'R': |
| /* Skip over the registers block. */ |
| dataptr += register_cache_size (); |
| break; |
| case 'M': |
| /* Skip over the memory block. */ |
| dataptr += sizeof (CORE_ADDR); |
| memcpy (&mlen, dataptr, sizeof (mlen)); |
| dataptr += (sizeof (mlen) + mlen); |
| break; |
| case 'S': |
| /* Skip over the static trace data block. */ |
| memcpy (&mlen, dataptr, sizeof (mlen)); |
| dataptr += (sizeof (mlen) + mlen); |
| break; |
| case 'V': |
| /* Skip over the TSV block. */ |
| dataptr += (sizeof (int) + sizeof (LONGEST)); |
| break; |
| default: |
| trace_debug ("traceframe %d has unknown block type 0x%x", |
| tfnum, blocktype); |
| return NULL; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| 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) |
| { |
| /* We don't like making up numbers, but GDB has all manner of |
| troubles when the target says there are no registers. */ |
| 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; |
| |
| dataptr = traceframe_find_regblock (tframe, -1); |
| if (dataptr == NULL) |
| return 0; |
| |
| init_register_cache (®cache, 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)); |
| |
| /* Check that requested data is in bounds. */ |
| if (maddr <= addr && (addr + length) <= (maddr + mlen)) |
| { |
| /* Block includes the requested range, copy it out. */ |
| memcpy (buf, dataptr + (addr - maddr), length); |
| *nbytes = length; |
| 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) |
| { |
| int tfnum; |
| struct traceframe *tframe; |
| unsigned char *database, *dataptr; |
| unsigned int datasize; |
| int vnum; |
| |
| trace_debug ("traceframe_read_tsv"); |
| |
| tfnum = 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 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)); |
| return 0; |
| } |
| |
| /* Skip over this block. */ |
| dataptr += sizeof (LONGEST); |
| } |
| |
| trace_debug ("traceframe %d has no data for variable %d", |
| tfnum, tsvnum); |
| return 1; |
| } |
| |
| static LONGEST |
| tsv_get_timestamp (void) |
| { |
| struct timeval tv; |
| |
| if (gettimeofday (&tv, 0) != 0) |
| return -1; |
| else |
| return (LONGEST) tv.tv_sec * 1000000 + tv.tv_usec; |
| } |
| |
| void |
| initialize_tracepoint (void) |
| { |
| /* There currently no way to change the buffer size. */ |
| const int sizeOfBuffer = 5 * 1024 * 1024; |
| unsigned char *buf = xmalloc (sizeOfBuffer); |
| init_trace_buffer (buf, sizeOfBuffer); |
| |
| /* 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); |
| set_trace_state_variable_name (1, "trace_timestamp"); |
| set_trace_state_variable_getter (1, tsv_get_timestamp); |
| } |