diff --git a/gdb/.Sanitize b/gdb/.Sanitize index 52000d3775..23da853246 100644 --- a/gdb/.Sanitize +++ b/gdb/.Sanitize @@ -428,8 +428,10 @@ z8k-tdep.c Things-to-lose: -HOW_TO_RELEASE ser-go32-para.c +HOW_TO_RELEASE +tracepoint.c +tracepoint.h Do-last: diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c new file mode 100644 index 0000000000..4b7a985182 --- /dev/null +++ b/gdb/tracepoint.c @@ -0,0 +1,1864 @@ +/* Tracing functionality for remote targets in custom GDB protocol + Copyright 1997 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 2 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, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "symtab.h" +#include "frame.h" +#include "tracepoint.h" +#include "gdbtypes.h" +#include "expression.h" +#include "gdbcmd.h" +#include "value.h" +#include "target.h" +#include "language.h" + +/* readline include files */ +#include "readline.h" +#include "history.h" + +/* readline defines this. */ +#undef savestring + +#ifdef HAVE_UNISTD_H +#include +#endif + +extern int info_verbose; + +/* If this definition isn't overridden by the header files, assume + that isatty and fileno exist on this system. */ +#ifndef ISATTY +#define ISATTY(FP) (isatty (fileno (FP))) +#endif + +/* Walk the following statement or block through all tracepoints. + ALL_TRACEPOINTS_SAFE does so even if the statment deletes the current + breakpoint. */ + +#define ALL_TRACEPOINTS(t) for (t = tracepoint_chain; t; t = t->next) + +#define ALL_TRACEPOINTS_SAFE(t,tmp) \ + for (t = tracepoint_chain; \ + t ? (tmp = t->next, 1) : 0;\ + t = tmp) + +/* Chain of all tracepoints defined. */ +struct tracepoint *tracepoint_chain; + +/* Number of last tracepoint made. */ +static int tracepoint_count; + +/* Number of last traceframe collected. */ +static int traceframe_number; + +/* Utility: returns true if "target remote" */ +static int +target_is_remote () +{ + if (current_target.to_shortname && + strcmp (current_target.to_shortname, "remote") == 0) + return 1; + else + return 0; +} + +/* Utility: generate error from an incoming stub packet. */ +static void +trace_error (buf) + char *buf; +{ + if (*buf++ != 'E') + return; /* not an error msg */ + switch (*buf) + { + case '1': /* malformed packet error */ + if (*++buf == '0') /* general case: */ + error ("tracepoint.c: badly formed packet."); + else + error ("tracepoint.c: badly formed packet at field #%d.", + *buf - '0'); + case '2': + error ("trace API error '%s'.", buf); + default: + error ("Target returns error code '%s'.", buf); + } +} + +/* Obsolete: collect regs from a trace frame */ +static void +trace_receive_regs (buf) + char *buf; +{ + long regno, i; + char regbuf[MAX_REGISTER_RAW_SIZE], *tmp, *p = buf; + + while (*p) + { + regno = strtol (p, &tmp, 16); + if (p == tmp || *tmp++ != ':') + error ("tracepoint.c: malformed 'R' packet"); + else p = tmp; + + for (i = 0; i < REGISTER_RAW_SIZE (regno); i++) + { + if (p[0] == 0 || p[1] == 0) + warning ("Remote reply is too short: %s", buf); + regbuf[i] = fromhex (p[0]) * 16 + fromhex (p[1]); + p += 2; + } + + if (*p++ != ';') + error ("tracepoint.c: malformed 'R' packet"); + + supply_register (regno, regbuf); + } +} + +/* Utility: wait for reply from stub, while accepting "O" packets */ +static void +remote_get_noisy_reply (buf) + char *buf; +{ + do /* loop on reply from remote stub */ + { + getpkt (buf, 0); + if (buf[0] == 0) + error ("Target does not support this command."); + else if (buf[0] == 'E') + trace_error (buf); + else if (buf[0] == 'R') + { + flush_cached_frames (); + registers_changed (); + select_frame (get_current_frame (), 0); + trace_receive_regs (buf); + } + else if (buf[0] == 'O' && + buf[1] != 'K') + remote_console_output (buf + 1); /* 'O' message from stub */ + else + return; /* here's the actual reply */ + } while (1); +} + +/* Set tracepoint count to NUM. */ +static void +set_tracepoint_count (num) + int num; +{ + tracepoint_count = num; + set_internalvar (lookup_internalvar ("tpnum"), + value_from_longest (builtin_type_int, (LONGEST) num)); +} + +/* Set traceframe number to NUM. */ +static void +set_traceframe_num (num) + int num; +{ + traceframe_number = num; + set_internalvar (lookup_internalvar ("trace_frame"), + value_from_longest (builtin_type_int, (LONGEST) num)); +} + +/* Low level routine to set a tracepoint. + Returns the tracepoint object so caller can set other things. + Does not set the tracepoint number! + Does not print anything. + + ==> This routine should not be called if there is a chance of later + error(); otherwise it leaves a bogus tracepoint on the chain. Validate + your arguments BEFORE calling this routine! */ + +static struct tracepoint * +set_raw_tracepoint (sal) + struct symtab_and_line sal; +{ + register struct tracepoint *t, *tc; + struct cleanup *old_chain; + + t = (struct tracepoint *) xmalloc (sizeof (struct tracepoint)); + old_chain = make_cleanup (free, t); + memset (t, 0, sizeof (*t)); + t->address = sal.pc; + if (sal.symtab == NULL) + t->source_file = NULL; + else + t->source_file = savestring (sal.symtab->filename, + strlen (sal.symtab->filename)); + t->language = current_language->la_language; + t->input_radix = input_radix; + t->line_number = sal.line; + t->enabled = enabled; + t->next = 0; + t->step_count = 0; + t->pass_count = 0; + + /* Add this tracepoint to the end of the chain + so that a list of tracepoints will come out in order + of increasing numbers. */ + + tc = tracepoint_chain; + if (tc == 0) + tracepoint_chain = t; + else + { + while (tc->next) + tc = tc->next; + tc->next = t; + } + discard_cleanups (old_chain); + return t; +} + +static void +trace_command (arg, from_tty) + char *arg; + int from_tty; +{ + char **canonical = (char **)NULL; + struct symtabs_and_lines sals; + struct symtab_and_line sal; + struct tracepoint *t; + char *addr_start = 0, *addr_end = 0, *cond_start = 0, *cond_end = 0; + int i; + + if (!arg || !*arg) + error ("trace command requires an argument"); + + if (from_tty && info_verbose) + printf_filtered ("TRACE %s\n", arg); + + if (arg[0] == '/') + { + return; + } + + addr_start = arg; + sals = decode_line_1 (&arg, 1, (struct symtab *)NULL, 0, &canonical); + addr_end = arg; + if (! sals.nelts) + return; /* ??? Presumably decode_line_1 has already warned? */ + + /* Resolve all line numbers to PC's */ + for (i = 0; i < sals.nelts; i++) + resolve_sal_pc (&sals.sals[i]); + + /* Now set all the tracepoints. */ + for (i = 0; i < sals.nelts; i++) + { + sal = sals.sals[i]; + + t = set_raw_tracepoint (sal); + set_tracepoint_count (tracepoint_count + 1); + t->number = tracepoint_count; + + /* If a canonical line spec is needed use that instead of the + command string. */ + if (canonical != (char **)NULL && canonical[i] != NULL) + t->addr_string = canonical[i]; + else if (addr_start) + t->addr_string = savestring (addr_start, addr_end - addr_start); + if (cond_start) + t->cond_string = savestring (cond_start, cond_end - cond_start); + } + + if (sals.nelts > 1) + { + printf_filtered ("Multiple tracepoints were set.\n"); + printf_filtered ("Use the \"delete\" command to delete unwanted tracepoints.\n"); + } +} + +static void +tracepoints_info (tpnum_exp, from_tty) + char *tpnum_exp; + int from_tty; +{ + struct tracepoint *t; + struct action_line *action; + int found_a_tracepoint = 0; + char wrap_indent[80]; + struct symbol *sym; + int tpnum = -1; +#if 0 + char *i1 = "\t", *i2 = "\t "; + char *indent, *actionline;; +#endif + + if (tpnum_exp) + tpnum = parse_and_eval_address (tpnum_exp); + + ALL_TRACEPOINTS (t) + if (tpnum == -1 || tpnum == t->number) + { + extern int addressprint; /* print machine addresses? */ + + if (!found_a_tracepoint++) + printf_filtered (" *** [info tracepoints header line] ***\n"); + + strcpy (wrap_indent, " "); + if (addressprint) + strcat (wrap_indent, " "); + + printf_filtered ("%-3d %-10s ", t->number, + t->enabled == enabled ? "enabled" : "disabled"); + if (addressprint) + { /* FIXME-32x64: need a print_address_numeric with field width */ + printf_filtered ("%s ", local_hex_string_custom ((unsigned long) t->address, "08l")); + } + if (t->source_file) + { + sym = find_pc_function (t->address); + if (sym) + { + fputs_filtered ("in ", gdb_stdout); + fputs_filtered (SYMBOL_SOURCE_NAME (sym), gdb_stdout); + wrap_here (wrap_indent); + fputs_filtered (" at ", gdb_stdout); + } + fputs_filtered (t->source_file, gdb_stdout); + printf_filtered (":%d", t->line_number); + } + else + print_address_symbolic (t->address, gdb_stdout, demangle, " "); + + if (t->pass_count != 0) + printf_filtered (" passcount = %d", t->pass_count); + printf_filtered ("\n"); + if (t->actions) + { + printf_filtered (" Actions for tracepoint %d: \n", t->number); +/* indent = i1; */ + for (action = t->actions; action; action = action->next) + { +#if 0 + actionline = action->action; + while (isspace(*actionline)) + actionline++; + + printf_filtered ("%s%s\n", indent, actionline); + if (0 == strncasecmp (actionline, "while-stepping", 14)) + indent = i2; + else if (0 == strncasecmp (actionline, "end", 3)) + indent = i1; +#else + printf_filtered ("\t%s\n", action->action); +#endif + } + } + } + if (!found_a_tracepoint) + { + if (tpnum == -1) + printf_filtered ("No tracepoints.\n"); + else + printf_filtered ("No tracepoint number %d.\n", tpnum); + } +} + +/* Optimization: the code to parse an enable, disable, or delete TP command + is virtually identical except for whether it performs an enable, disable, + or delete. Therefore I've combined them into one function with an opcode. + */ +enum tracepoint_opcode +{ + enable, + disable, + delete +}; + +/* This function implements enable, disable and delete. */ +static void +tracepoint_operation (t, from_tty, opcode) + struct tracepoint *t; + int from_tty; + enum tracepoint_opcode opcode; +{ + struct tracepoint *t2; + struct action_line *action, *next; + + switch (opcode) { + case enable: + t->enabled = enabled; + break; + case disable: + t->enabled = disabled; + break; + case delete: + if (tracepoint_chain == t) + tracepoint_chain = t->next; + + ALL_TRACEPOINTS (t2) + if (t2->next == t) + { + t2->next = t->next; + break; + } + if (t->cond_string) + free (t->cond_string); + if (t->addr_string) + free (t->addr_string); + if (t->source_file) + free (t->source_file); + for (action = t->actions; action; action = next) + { + next = action->next; + if (action->action) + free (action->action); + free (action); + } + free (t); + break; + } +} + +/* Utility: parse a tracepoint number and look it up in the list. */ +static struct tracepoint * +get_tracepoint_by_number (arg) + char **arg; +{ + struct tracepoint *t; + char *cp; + value_ptr val; + int tpnum; + + if (arg == 0) + error ("Bad tracepoint argument"); + + if (*arg == 0 || **arg == 0) /* empty arg means refer to last tp */ + tpnum = tracepoint_count; + else if (**arg == '$') /* handle convenience variable */ + { + cp = *arg + 1; + /* find end of convenience variable name */ + while (**arg && **arg != ' ' && **arg != '\t') + *arg++; + /* null-terminate if necessary */ + if (**arg != 0) + *(*arg++) = 0; + val = value_of_internalvar (lookup_internalvar (cp)); + if (TYPE_CODE( VALUE_TYPE (val)) != TYPE_CODE_INT) + error ("Convenience variable must have integral type."); + tpnum = (int) value_as_long (val); + } + else /* handle tracepoint number */ + { + tpnum = strtol (*arg, arg, 10); + } + ALL_TRACEPOINTS (t) + if (t->number == tpnum) + { + return t; + } + warning ("No tracepoint number %d.\n", tpnum); + return NULL; +} + +/* Utility: parse a list of tracepoint numbers, and call a func for each. */ +static void +map_args_over_tracepoints (args, from_tty, opcode) + char *args; + int from_tty; + enum tracepoint_opcode opcode; +{ + struct tracepoint *t; + int tpnum; + char *cp; + + if (args == 0 || *args == 0) /* do them all */ + ALL_TRACEPOINTS (t) + tracepoint_operation (t, from_tty, opcode); + else + while (*args) + { + if (t = get_tracepoint_by_number (&args)) + tracepoint_operation (t, from_tty, opcode); + while (*args == ' ' || *args == '\t') + args++; + } +} + +static void +enable_trace_command (args, from_tty) + char *args; + int from_tty; +{ + dont_repeat (); + map_args_over_tracepoints (args, from_tty, enable); +} + +static void +disable_trace_command (args, from_tty) + char *args; + int from_tty; +{ + dont_repeat (); + map_args_over_tracepoints (args, from_tty, disable); +} + +static void +delete_trace_command (args, from_tty) + char *args; + int from_tty; +{ + dont_repeat (); + if (!args || !*args) + if (!query ("Delete all tracepoints? ")) + return; + + map_args_over_tracepoints (args, from_tty, delete); +} + +static void +trace_pass_command (args, from_tty) + char *args; + int from_tty; +{ + struct tracepoint *t1 = (struct tracepoint *) -1, *t2; + unsigned long count; + + if (args == 0 || *args == 0) + error ("PASS command requires an argument (count + optional TP num)"); + + count = strtoul (args, &args, 10); /* count comes first, then TP num */ + + while (*args && isspace (*args)) + args++; + + if (*args && strncasecmp (args, "all", 3) == 0) + args += 3; /* skip special argument "all" */ + else + t1 = get_tracepoint_by_number (&args); + + if (t1 == NULL) + return; /* error, bad tracepoint number */ + + ALL_TRACEPOINTS (t2) + if (t1 == (struct tracepoint *) -1 || t1 == t2) + { + t2->pass_count = count; + if (from_tty) + printf_filtered ("Setting tracepoint %d's passcount to %d\n", + t2->number, count); + } +} + +/* ACTIONS ACTIONS ACTIONS */ + +static void read_actions PARAMS((struct tracepoint *)); +static void free_actions PARAMS((struct tracepoint *)); +static int validate_actionline PARAMS((char *, struct tracepoint *)); + +static void +trace_actions_command (args, from_tty) + char *args; + int from_tty; +{ + struct tracepoint *t; + char *actions; + + if (t = get_tracepoint_by_number (&args)) + { + if (from_tty) + printf_filtered ("Enter actions for tracepoint %d, one per line.\n", + t->number); + free_actions (t); + read_actions (t); + /* tracepoints_changed () */ + } + /* else error, just return; */ +} + +enum actionline_type +{ + BADLINE = -1, + GENERIC = 0, + END = 1, + STEPPING = 2, +}; + +static void +read_actions (t) + struct tracepoint *t; +{ + char *line; + char *prompt1 = "> ", *prompt2 = " > "; + char *prompt = prompt1; + enum actionline_type linetype; + extern FILE *instream; + struct action_line *next = NULL, *temp; + struct cleanup *old_chain; + + /* Control-C quits instantly if typed while in this loop + since it should not wait until the user types a newline. */ + immediate_quit++; +#ifdef STOP_SIGNAL + if (job_control) + signal (STOP_SIGNAL, stop_sig); +#endif + old_chain = make_cleanup (free_actions, (void *) t); + while (1) + { + /* Make sure that all output has been output. Some machines may let + you get away with leaving out some of the gdb_flush, but not all. */ + wrap_here (""); + gdb_flush (gdb_stdout); + gdb_flush (gdb_stderr); + if (instream == stdin && ISATTY (instream)) + line = readline (prompt); + else + line = gdb_readline (0); + + linetype = validate_actionline (line, t); + if (linetype == BADLINE) + continue; /* already warned -- collect another line */ + + temp = xmalloc (sizeof (struct action_line)); + temp->next = NULL; + temp->action = line; + + if (next == NULL) /* first action for this tracepoint? */ + t->actions = next = temp; + else + { + next->next = temp; + next = temp; + } + + if (linetype == STEPPING) /* begin "while-stepping" */ + if (prompt == prompt2) + { + warning ("Already processing 'while-stepping'"); + continue; + } + else + prompt = prompt2; /* change prompt for stepping actions */ + else if (linetype == END) + if (prompt == prompt2) + prompt = prompt1; /* end of single-stepping actions */ + else + break; /* end of actions */ + } +#ifdef STOP_SIGNAL + if (job_control) + signal (STOP_SIGNAL, SIG_DFL); +#endif + immediate_quit = 0; + discard_cleanups (old_chain); +} + +static enum actionline_type +validate_actionline (line, t) + char *line; + struct tracepoint *t; +{ + char *p; + struct expression *exp; + value_ptr temp, temp2; + + for (p = line; isspace (*p); ) + p++; + + /* symbol lookup etc. */ + if (*p == '\0') /* empty line: just prompt for another line. */ + return BADLINE; + else if (0 == strncasecmp (p, "collect", 7)) + { + p += 7; + do { /* repeat over a comma-separated list */ + while (isspace (*p)) + p++; + + if (*p == '$' && /* look for special pseudo-symbols */ + ((0 == strncasecmp ("reg", p + 1, 3)) || + (0 == strncasecmp ("arg", p + 1, 3)) || + (0 == strncasecmp ("loc", p + 1, 3)))) + p = (char *) strchr (p, ','); + else + { + exp = parse_exp_1 (&p, block_for_pc (t->address), 1); + if (exp->elts[0].opcode != OP_VAR_VALUE && + /*exp->elts[0].opcode != OP_LONG && */ + /*exp->elts[0].opcode != UNOP_CAST && */ + exp->elts[0].opcode != OP_REGISTER) + { + warning ("collect: enter variable name or register.\n"); + return BADLINE; + } + if (exp->elts[0].opcode == OP_VAR_VALUE) + if (SYMBOL_CLASS (exp->elts[2].symbol) == LOC_CONST) + { + warning ("%s is constant (value %d): will not be collected.", + SYMBOL_NAME (exp->elts[2].symbol), + SYMBOL_VALUE (exp->elts[2].symbol)); + return BADLINE; + } + else if (SYMBOL_CLASS (exp->elts[2].symbol) == LOC_OPTIMIZED_OUT) + { + warning ("%s is optimized away and cannot be collected.", + SYMBOL_NAME (exp->elts[2].symbol)); + return BADLINE; + } + } + } while (p && *p++ == ','); + return GENERIC; + } + else if (0 == strncasecmp (p, "while-stepping", 14)) + { + char *steparg; /* in case warning is necessary */ + + p += 14; + while (isspace (*p)) + p++; + steparg = p; + + if (*p) + { + t->step_count = strtol (p, &p, 0); + if (t->step_count == 0) + { + warning ("'%s' evaluates to zero -- command ignored."); + return BADLINE; + } + } + else + t->step_count = -1; + return STEPPING; + } + else if (0 == strncasecmp (p, "end", 3)) + return END; + else + { + warning ("'%s' is not a supported tracepoint action.", p); + return BADLINE; + } +} + +static void +free_actions (t) + struct tracepoint *t; +{ + struct action_line *line, *next; + + for (line = t->actions; line; line = next) + { + next = line->next; + free (line); + } + t->actions = NULL; +} + +struct memrange { + int type; /* 0 for absolute memory range, else basereg number */ + bfd_signed_vma start; + bfd_signed_vma end; +}; + +struct collection_list { + unsigned char regs_mask[8]; /* room for up to 256 regs */ + long listsize; + long next_memrange; + struct memrange *list; +} tracepoint_list, stepping_list; + +static int +memrange_cmp (a, b) + struct memrange *a, *b; +{ + if (a->type < b->type) return -1; + if (a->type > b->type) return 1; + if (a->type == 0) + { + if ((bfd_vma) a->start < (bfd_vma) b->start) return -1; + if ((bfd_vma) a->start > (bfd_vma) b->start) return 1; + } + else + { + if (a->start < b->start) return -1; + if (a->start > b->start) return 1; + } + return 0; +} + +static void +memrange_sortmerge (memranges) + struct collection_list *memranges; +{ + int a, b; + + qsort (memranges->list, memranges->next_memrange, + sizeof (struct memrange), memrange_cmp); + if (memranges->next_memrange > 0) + { + for (a = 0, b = 1; b < memranges->next_memrange; b++) + { + if (memranges->list[a].type == memranges->list[b].type && + memranges->list[b].start - memranges->list[a].end <= + MAX_REGISTER_VIRTUAL_SIZE) + { + memranges->list[a].end = memranges->list[b].end; + continue; /* next b, same a */ + } + a++; /* next a */ + if (a != b) + memcpy (&memranges->list[a], &memranges->list[b], + sizeof (struct memrange)); + } + memranges->next_memrange = a + 1; + } +} + +void +add_register (collection, regno) + struct collection_list *collection; + unsigned long regno; +{ + if (info_verbose) + printf_filtered ("collect register %d\n", regno); + if (regno > (8 * sizeof (collection->regs_mask))) + error ("Internal: register number %d too large for tracepoint", + regno); + collection->regs_mask [regno / 8] |= 1 << (regno % 8); +} + +static void +add_memrange (memranges, type, base, len) + struct collection_list *memranges; + int type; + bfd_signed_vma base; + unsigned long len; +{ + if (info_verbose) + printf_filtered ("(%d,0x%x,%d)\n", type, base, len); + /* type: 0 == memory, n == basereg */ + memranges->list[memranges->next_memrange].type = type; + /* base: addr if memory, offset if reg relative. */ + memranges->list[memranges->next_memrange].start = base; + /* len: we actually save end (base + len) for convenience */ + memranges->list[memranges->next_memrange].end = base + len; + memranges->next_memrange++; + if (memranges->next_memrange >= memranges->listsize) + { + memranges->listsize *= 2; + memranges->list = xrealloc (memranges->list, + memranges->listsize); + } + + if (type != 0) /* better collect the base register! */ + add_register (memranges, type); +} + +static void +collect_symbol (collect, sym) + struct collection_list *collect; + struct symbol *sym; +{ + unsigned long len; + unsigned long reg; + bfd_signed_vma offset; + + len = TYPE_LENGTH (check_typedef (SYMBOL_TYPE (sym))); + switch (SYMBOL_CLASS (sym)) { + default: + printf_filtered ("%s: don't know symbol class %d\n", + SYMBOL_NAME (sym), SYMBOL_CLASS (sym)); + break; + case LOC_CONST: + printf_filtered ("%s is constant, value is %d: will not be collected.\n", + SYMBOL_NAME (sym), SYMBOL_VALUE (sym)); + break; + case LOC_STATIC: + offset = SYMBOL_VALUE_ADDRESS (sym); + if (info_verbose) + printf_filtered ("LOC_STATIC %s: collect %d bytes " + "at 0x%08x\n", + SYMBOL_NAME (sym), len, offset); + add_memrange (collect, 0, offset, len); /* 0 == memory */ + break; + case LOC_REGISTER: + case LOC_REGPARM: + reg = SYMBOL_VALUE (sym); + if (info_verbose) + printf_filtered ("LOC_REG[parm] %s: ", SYMBOL_NAME (sym)); + add_register (collect, reg); + break; + case LOC_ARG: + case LOC_REF_ARG: + printf_filtered ("Sorry, don't know how to do LOC_ARGs yet.\n"); + printf_filtered (" (will not collect %s)\n", + SYMBOL_NAME (sym)); + break; + case LOC_REGPARM_ADDR: + reg = SYMBOL_VALUE (sym); + offset = 0; + if (info_verbose) + { + printf_filtered ("LOC_REGPARM_ADDR %s: Collect %d bytes at offset %d from reg %d\n", + SYMBOL_NAME (sym), len, offset, reg); + } + add_memrange (collect, reg, offset, len); + break; + case LOC_LOCAL: + case LOC_LOCAL_ARG: + offset = SYMBOL_VALUE (sym); + reg = FP_REGNUM; + if (info_verbose) + { + printf_filtered ("LOC_LOCAL %s: Collect %d bytes at offset %d from frame ptr reg %d\n", + SYMBOL_NAME (sym), len, offset, reg); + } + add_memrange (collect, reg, offset, len); + break; + case LOC_BASEREG: + case LOC_BASEREG_ARG: + reg = SYMBOL_BASEREG (sym); + offset = SYMBOL_VALUE (sym); + if (info_verbose) + { + printf_filtered ("LOC_BASEREG %s: collect %d bytes at offset %d from basereg %d\n", + SYMBOL_NAME (sym), len, offset, reg); + } + add_memrange (collect, reg, offset, len); + break; + case LOC_UNRESOLVED: + printf_filtered ("Don't know LOC_UNRESOLVED %s\n", SYMBOL_NAME (sym)); + break; + case LOC_OPTIMIZED_OUT: + printf_filtered ("%s has been optimized out of existance.\n", + SYMBOL_NAME (sym)); + break; + } +} + +static void +add_local_symbols (collect, pc, type) + struct collection_list *collect; + CORE_ADDR pc; + char type; +{ + struct symbol *sym; + struct block *block; + int i, nsyms, count = 0; + + block = block_for_pc (pc); + while (block != 0) + { + nsyms = BLOCK_NSYMS (block); + for (i = 0; i < nsyms; i++) + { + sym = BLOCK_SYM (block, i); + switch (SYMBOL_CLASS (sym)) { + case LOC_LOCAL: + case LOC_STATIC: + case LOC_REGISTER: + case LOC_BASEREG: + if (type == 'L') /* collecting Locals */ + { + count++; + collect_symbol (collect, sym); + } + break; + case LOC_ARG: + case LOC_LOCAL_ARG: + case LOC_REF_ARG: + case LOC_REGPARM: + case LOC_REGPARM_ADDR: + case LOC_BASEREG_ARG: + if (type == 'A') /* collecting Arguments */ + { + count++; + collect_symbol (collect, sym); + } + } + } + if (BLOCK_FUNCTION (block)) + break; + else + block = BLOCK_SUPERBLOCK (block); + } + if (count == 0) + warning ("No %s found in scope.", type == 'L' ? "locals" : "args"); +} + +static void +clear_collection_list (list) + struct collection_list *list; +{ + list->next_memrange = 0; + memset (list->regs_mask, 0, sizeof (list->regs_mask)); +} + +static char * +stringify_collection_list (list, string) + struct collection_list *list; + char *string; +{ + char *end = string; + long i; + + for (i = sizeof (list->regs_mask) - 1; i > 0; i--) + if (list->regs_mask[i] != 0) /* skip leading zeroes in regs_mask */ + break; + if (list->regs_mask[i] != 0) /* prepare to send regs_mask to the stub */ + { + if (info_verbose) + printf_filtered ("\nCollecting registers (mask): 0x"); + *end++='R'; + for (; i >= 0; i--) + { + if (info_verbose) + printf_filtered ("%02X", list->regs_mask[i]); + sprintf (end, "%02X", list->regs_mask[i]); + end += 2; + } + } + if (info_verbose) + printf_filtered ("\n"); + if (list->next_memrange > 0 && info_verbose) + printf_filtered ("Collecting memranges: \n"); + for (i = 0; i < list->next_memrange; i++) + { + if (info_verbose) + printf_filtered ("(%d, 0x%x, %d)\n", + list->list[i].type, + list->list[i].start, + list->list[i].end - list->list[i].start); + sprintf (end, "M%X,%X,%X", + list->list[i].type, + list->list[i].start, + list->list[i].end - list->list[i].start); + end += strlen (end); + } + if (end == string) + return NULL; + else + return string; +} + +static void +encode_actions (t, tdp_actions, step_count, stepping_actions) + struct tracepoint *t; + char **tdp_actions; + unsigned long *step_count; + char **stepping_actions; +{ + struct expression *exp; + static char tdp_buff[2048], step_buff[2048]; + struct action_line *action; + char *action_exp; + bfd_signed_vma offset; + long i; + struct collection_list *collect; + + clear_collection_list (&tracepoint_list); + clear_collection_list (&stepping_list); + collect = &tracepoint_list; + + *tdp_actions = NULL; + *stepping_actions = NULL; + + for (action = t->actions; action; action = action->next) + { + action_exp = action->action; + while (isspace (*action_exp)) + action_exp++; + + if (0 == strncasecmp (action_exp, "collect", 7)) + { + action_exp = action_exp + 7; + do { /* repeat over a comma-separated list */ + while (isspace (*action_exp)) + action_exp++; + + if (0 == strncasecmp ("$reg", action_exp, 4)) + { + for (i = 0; i < NUM_REGS; i++) + add_register (collect, i); + action_exp = (char *) strchr (action_exp, ','); /* more? */ + } + else if (0 == strncasecmp ("$arg", action_exp, 4)) + { + add_local_symbols (collect, t->address, 'A'); + action_exp = (char *) strchr (action_exp, ','); /* more? */ + } + else if (0 == strncasecmp ("$loc", action_exp, 4)) + { + add_local_symbols (collect, t->address, 'L'); + action_exp = (char *) strchr (action_exp, ','); /* more? */ + } + else + { + unsigned long addr, len; + + exp = parse_exp_1 (&action_exp, block_for_pc (t->address), 1); + switch (exp->elts[0].opcode) { + case OP_REGISTER: + i = exp->elts[1].longconst; + if (info_verbose) + printf_filtered ("OP_REGISTER: "); + add_register (collect, i); + break; + case OP_VAR_VALUE: + collect_symbol (collect, exp->elts[2].symbol); + break; +#if 0 + case OP_LONG: + addr = exp->elts[2].longconst; + if (*action_exp == ':') + { + exp = parse_exp_1 (&action_exp, + block_for_pc (t->address), + 1); + if (exp->elts[0].opcode == OP_LONG) + len = exp->elts[2].longconst; + else + error ("length field requires a literal long const"); + } + else + len = 4; + + add_memrange (collect, 0, addr, len); + break; +#endif + } + } + } while (action_exp && *action_exp++ == ','); + } + else if (0 == strncasecmp (action_exp, "while-stepping", 14)) + { + collect = &stepping_list; + } + else if (0 == strncasecmp (action_exp, "end", 3)) + { + if (collect == &stepping_list) /* end stepping actions */ + collect = &tracepoint_list; + else + break; /* end tracepoint actions */ + } + } + memrange_sortmerge (&tracepoint_list); + memrange_sortmerge (&stepping_list); + + *tdp_actions = stringify_collection_list (&tracepoint_list, &tdp_buff); + *stepping_actions = stringify_collection_list (&stepping_list, &step_buff); +} + +static char target_buf[2048]; + +static void +trace_start_command (args, from_tty) + char *args; + int from_tty; +{ /* STUB_COMM MOSTLY_IMPLEMENTED */ + struct tracepoint *t; + char buf[2048]; + char *tdp_actions; + char *stepping_actions; + unsigned long step_count; + + dont_repeat (); /* like "run", dangerous to repeat accidentally */ + + if (target_is_remote ()) + { + putpkt ("QTinit"); + remote_get_noisy_reply (target_buf); + if (strcmp (target_buf, "OK")) + error ("Target does not support this command."); + + ALL_TRACEPOINTS (t) + { + int ss_count; /* if actions include singlestepping */ + int disable_mask; /* ??? */ + int enable_mask; /* ??? */ + + sprintf (buf, "QTDP:%x:%x:%c:%x:%x", t->number, t->address, + t->enabled == enabled ? 'E' : 'D', + t->step_count, t->pass_count); + if (t->actions) + { + encode_actions (t, &tdp_actions, &step_count, &stepping_actions); + /* do_single_steps (t); */ + if (tdp_actions) + { + if (strlen (buf) + strlen (tdp_actions) >= sizeof (buf)) + error ("Actions for tracepoint %d too complex; " + "please simplify.", t->number); + strcat (buf, tdp_actions); + } + if (stepping_actions) + { + strcat (buf, "S"); + if (strlen (buf) + strlen (stepping_actions) >= sizeof (buf)) + error ("Actions for tracepoint %d too complex; " + "please simplify.", t->number); + strcat (buf, stepping_actions); + } + } + putpkt (buf); + remote_get_noisy_reply (target_buf); + if (strcmp (target_buf, "OK")) + error ("Target does not support tracepoints."); + } + putpkt ("QTStart"); + remote_get_noisy_reply (target_buf); + if (strcmp (target_buf, "OK")) + error ("Bogus reply from target: %s", target_buf); + } + else + printf_filtered ("Trace can only be run on remote targets.\n"); +} + +static void +trace_stop_command (args, from_tty) + char *args; + int from_tty; +{ /* STUB_COMM IS_IMPLEMENTED */ + if (target_is_remote ()) + { + putpkt ("QTStop"); + remote_get_noisy_reply (target_buf); + if (strcmp (target_buf, "OK")) + error ("Bogus reply from target: %s", target_buf); + } + else + error ("Trace can only be run on remote targets."); +} + +static void +trace_status_command (args, from_tty) + char *args; + int from_tty; +{ /* STUB_COMM IS_IMPLEMENTED */ + if (target_is_remote ()) + { + putpkt ("qTStatus"); + remote_get_noisy_reply (target_buf); + if (strcmp (target_buf, "OK")) + error ("Bogus reply from target: %s", target_buf); + } + else + error ("Trace can only be run on remote targets."); +} + +static void +trace_buff_command (args, from_tty) + char *args; + int from_tty; +{ /* STUB_COMM NOT_IMPLEMENTED */ + if (args == 0 || *args == 0) + printf_filtered ("TBUFFER command requires argument (on or off)\n"); + else if (strcasecmp (args, "on") == 0) + printf_filtered ("tbuffer overflow on.\n"); + else if (strcasecmp (args, "off") == 0) + printf_filtered ("tbuffer overflow off.\n"); + else + printf_filtered ("TBUFFER: unknown argument (use on or off)\n"); +} + +static void +trace_limit_command (args, from_tty) + char *args; + int from_tty; +{ /* STUB_COMM NOT_IMPLEMENTED */ + printf_filtered ("Limit it to what?\n"); +} + +static void +trace_find_command (args, from_tty) + char *args; + int from_tty; +{ /* STUB_COMM PART_IMPLEMENTED */ + /* this should only be called with a numeric argument */ + int frameno, target_frameno; + char buf[40], *tmp; + + if (target_is_remote ()) + { + if (args == 0 || *args == 0) + frameno = traceframe_number + 1; + else if (*args != '-' && !isdigit(*args)) + error ("tfind requires a literal (for now): %s rejected.", args); + else + frameno = strtol (args, 0, 0); /* for now, literals only */ + + sprintf (buf, "QTFrame:%x", frameno); + putpkt (buf); + remote_get_noisy_reply (target_buf); + + if (target_buf[0] != 'F') + error ("Bogus reply from target: %s", target_buf); + target_frameno = strtol (&target_buf[1], &tmp, 16); + if (tmp == &target_buf[1]) + error ("Bogus reply from target: %s", target_buf); + if (target_frameno != frameno) + warning ("Target replied with different framenumber, %s != %x", + target_buf, frameno); + + set_traceframe_num (target_frameno); + flush_cached_frames (); + registers_changed (); + select_frame (get_current_frame (), 0); + } + else + error ("Trace can only be run on remote targets."); +} + +static void +trace_find_pc_command (args, from_tty) + char *args; + int from_tty; +{ /* STUB_COMM PART_IMPLEMENTED */ + CORE_ADDR pc; + int target_frameno; + char buf[40], *tmp; + + if (target_is_remote ()) + { + if (args == 0 || *args == 0) + { /* TFIND PC is the same as TFIND */ + trace_find_command (args, from_tty); + return; + } + if (!isdigit(*args)) + error ("tfind pc requires a literal argument (for now): %s rejected.", + args); + + pc = strtol (args, 0, 0); /* for now, literals only */ + sprintf (buf, "QTFrame:pc:%x", pc); + putpkt (buf); + remote_get_noisy_reply (target_buf); + + if (target_buf[0] != 'F') + error ("Bogus reply from target: %s", target_buf); + target_frameno = strtol (&target_buf[1], &tmp, 16); + if (tmp == &target_buf[1]) + error ("Bogus reply from target: %s", target_buf); + + set_traceframe_num (target_frameno); + flush_cached_frames (); + registers_changed (); + select_frame (get_current_frame (), 0); + } + else + error ("Trace can only be run on remote targets."); +} + +static void +trace_find_tdp_command (args, from_tty) + char *args; + int from_tty; +{ /* STUB_COMM PART_IMPLEMENTED */ + int target_frameno, tdp; + char buf[40], *tmp; + + if (target_is_remote ()) + { + if (args == 0 || *args == 0) + { /* TFIND TDP is the same as TFIND */ + trace_find_command (args, from_tty); + return; + } + if (!isdigit(*args)) + error ("tfind tdp command requires a literal argument (for now): %s", + args); + + tdp = strtol (args, 0, 0); /* for now, literals only */ + sprintf (buf, "QTFrame:tdp:%x", tdp); + putpkt (buf); + remote_get_noisy_reply (target_buf); + + if (target_buf[0] != 'F') + error ("Bogus reply from target: %s", target_buf); + target_frameno = strtol (&target_buf[1], &tmp, 16); + if (tmp == &target_buf[1]) + error ("Bogus reply from target: %s", target_buf); + + set_traceframe_num (target_frameno); + flush_cached_frames (); + registers_changed (); + select_frame (get_current_frame (), 0); + } + else + error ("Trace can only be run on remote targets."); +} + +static void +trace_find_range_command (args, from_tty) + char *args; + int from_tty; +{ /* STUB_COMM PART_IMPLEMENTED */ + static CORE_ADDR start, stop; + int target_frameno; + char buf[50], *tmp; + + if (target_is_remote ()) + { + if (args == 0 || *args == 0 || !isdigit(*args)) + { /* XXX FIXME: what should default behavior be? */ + printf_filtered ("Usage: tfind range
\n"); + return; + } + + start = strtol (args, &args, 0); /* for now, literals only */ + while (args && *args && isspace (*args)) + args++; + + if (args == 0 || *args == 0 || !isdigit(*args)) + { + printf_filtered ("Usage: tfind range
\n"); + return; + } + + stop = strtol (args, &args, 0); /* for now, literals only */ + + sprintf (buf, "QTFrame:range:%x:%x", start, stop); + putpkt (buf); + remote_get_noisy_reply (target_buf); + + if (target_buf[0] != 'F') + error ("Bogus reply from target: %s", target_buf); + target_frameno = strtol (&target_buf[1], &tmp, 16); + if (tmp == &target_buf[1]) + error ("Bogus reply from target: %s", target_buf); + + set_traceframe_num (target_frameno); + flush_cached_frames (); + registers_changed (); + select_frame (get_current_frame (), 0); + } + else + error ("Trace can only be run on remote targets."); +} + +static void +trace_find_outside_command (args, from_tty) + char *args; + int from_tty; +{ /* STUB_COMM PART_IMPLEMENTED */ + CORE_ADDR start, stop; + int target_frameno; + char buf[50], *tmp; + + if (target_is_remote ()) + { + if (args == 0 || *args == 0 || !isdigit(*args)) + { /* XXX FIXME: what should default behavior be? */ + printf_filtered ("Usage: tfind outside
\n"); + return; + } + + start = strtol (args, &args, 0); + while (args && *args && isspace (*args)) + args++; + + if (args == 0 || *args == 0 || !isdigit(*args)) + { + printf_filtered ("Usage: tfind outside
\n"); + return; + } + + stop = strtol (args, &args, 0); + + sprintf (buf, "QTFrame:outside:%x:%x", start, stop); + putpkt (buf); + remote_get_noisy_reply (target_buf); + + if (target_buf[0] != 'F') + error ("Bogus reply from target: %s", target_buf); + target_frameno = strtol (&target_buf[1], &tmp, 16); + if (tmp == &target_buf[1]) + error ("Bogus reply from target: %s", target_buf); + + set_traceframe_num (target_frameno); + flush_cached_frames (); + registers_changed (); + select_frame (get_current_frame (), 0); + } + else + error ("Trace can only be run on remote targets."); +} + +static void +trace_display_command (args, from_tty) + char *args; + int from_tty; +{ /* STUB_COMM NOT_IMPLEMENTED */ + if (!target_is_remote ()) + { + printf_filtered ("Trace can only be run on remote targets.\n"); + return; + } + + if (args && *args) + printf_filtered ("Displaying trace as %s.\n", args); + else + printf_filtered ("Displaying trace.\n"); +} + +static void +tracepoint_save_command (args, from_tty) + char *args; + int from_tty; +{ + struct tracepoint *tp; + struct action_line *line; + FILE *fp; + char *i1 = " ", *i2 = " "; + char *indent, *actionline; + + if (args == 0 || *args == 0) + error ("Argument required (file name in which to save tracepoints"); + + if (tracepoint_chain == 0) + { + warning ("save-tracepoints: no tracepoints to save.\n"); + return; + } + + if (!(fp = fopen (args, "w"))) + error ("Unable to open file '%s' for saving tracepoints"); + + ALL_TRACEPOINTS (tp) + { + if (tp->addr_string) + fprintf (fp, "trace %s\n", tp->addr_string); + else + fprintf (fp, "trace *0x%x\n", tp->address); + + if (tp->pass_count) + fprintf (fp, " passcount %d\n", tp->pass_count); + + if (tp->actions) + { + fprintf (fp, " actions\n"); + indent = i1; + for (line = tp->actions; line; line = line->next) + { + actionline = line->action; + while (isspace(*actionline)) + actionline++; + + fprintf (fp, "%s%s\n", indent, actionline); + if (0 == strncasecmp (actionline, "while-stepping", 14)) + indent = i2; + else if (0 == strncasecmp (actionline, "end", 3)) + indent = i1; + } + } + } + fclose (fp); + if (from_tty) + printf_filtered ("Tracepoints saved to file '%s'.\n", args); + return; +} + +static void +scope_info (args, from_tty) + char *args; + int from_tty; +{ + struct symtab_and_line sal; + struct symtabs_and_lines sals; + struct symbol *sym; + struct block *block; + char **canonical, *save_args = args, *symname; + int i, nsyms, count = 0; + enum address_class aclass; + + if (args == 0 || *args == 0) + error ("requires an argument (function, line or *addr) to define a scope"); + + sals = decode_line_1 (&args, 1, NULL, 0, &canonical); + if (sals.nelts == 0) + return; /* presumably decode_line_1 has already warned */ + + printf_filtered ("Block for %s", save_args); + /* Resolve all line numbers to PC's */ + for (i = 0; i < sals.nelts; i++) + resolve_sal_pc (&sals.sals[i]); + + block = block_for_pc (sals.sals[0].pc); + while (block != 0) + { + nsyms = BLOCK_NSYMS (block); + for (i = 0; i < nsyms; i++) + { + count++; + sym = BLOCK_SYM (block, i); + switch (SYMBOL_CLASS (sym)) { + default: + case LOC_UNDEF: /* messed up symbol? */ + symname = SYMBOL_NAME (sym); + if (symname && *symname) /* guard against messed up name */ + printf_filtered ("Bogus symbol %s, class %d\n", + symname, SYMBOL_CLASS (sym)); + else + printf_filtered ("Completely bogus symbol, class %d.\n", + SYMBOL_CLASS (sym)); + count--; /* don't count this one */ + continue; + case LOC_CONST: printf_filtered ("\nConstant "); break; + case LOC_STATIC: printf_filtered ("\nStatic "); break; + case LOC_REGISTER: printf_filtered ("\nRegister "); break; + case LOC_ARG: printf_filtered ("\nArg "); break; + case LOC_REF_ARG: printf_filtered ("\nReference Arg "); break; + case LOC_REGPARM: printf_filtered ("\nRegister Arg "); break; + case LOC_REGPARM_ADDR: + printf_filtered ("\nIndirect Register Arg "); break; + case LOC_LOCAL: printf_filtered ("\nStack Local "); break; + case LOC_TYPEDEF: printf_filtered ("\nLocal Typedef "); break; + case LOC_LABEL: printf_filtered ("\nLocal Label "); break; + case LOC_BLOCK: printf_filtered ("\nLocal Function "); break; + case LOC_CONST_BYTES: printf_filtered ("\nLoc. Byte Seq. "); break; + case LOC_LOCAL_ARG: printf_filtered ("\nStack Arg "); break; + case LOC_BASEREG: printf_filtered ("\nBasereg Local "); break; + case LOC_BASEREG_ARG: printf_filtered ("\nBasereg Arg "); break; + case LOC_UNRESOLVED: + printf_filtered ("\nUnresolved Static "); break; + case LOC_OPTIMIZED_OUT: printf_filtered ("\nOptimized-Out "); break; + } + type_print (SYMBOL_TYPE (sym), SYMBOL_NAME (sym), gdb_stdout, -1); + } + if (BLOCK_FUNCTION (block)) + break; + else + block = BLOCK_SUPERBLOCK (block); + } + if (count <= 0) + printf_filtered (" contains no locals or arguments."); + printf_filtered ("\n"); +} + +static struct cmd_list_element *tfindlist; +static struct cmd_list_element *tracelist; + +void +_initialize_tracepoint () +{ + tracepoint_chain = 0; + tracepoint_count = 0; + traceframe_number = 0; + + set_internalvar (lookup_internalvar ("tpnum"), + value_from_longest (builtin_type_int, (LONGEST) 0)); + set_internalvar (lookup_internalvar ("trace_frame"), + value_from_longest (builtin_type_int, (LONGEST) 0)); + + if (tracepoint_list.list == NULL) + { + tracepoint_list.listsize = 128; + tracepoint_list.list = xmalloc + (tracepoint_list.listsize * sizeof (struct memrange)); + } + if (stepping_list.list == NULL) + { + stepping_list.listsize = 128; + stepping_list.list = xmalloc + (stepping_list.listsize * sizeof (struct memrange)); + } + + add_info ("scope", scope_info, + "List the variables local to a scope"); + +#if 1 + add_cmd ("tracepoints", class_trace, NO_FUNCTION, + "Tracing program execution without stopping the program.", + &cmdlist); + + add_info ("tracepoints", tracepoints_info, + "Display tracepoints, or tracepoint number NUMBER.\n" + "Convenience variable \"$tpnum\" contains the number of the\n" + "last tracepoint set."); + + add_info_alias ("tp", "tracepoints", 1); + + add_com ("save-tracepoints", class_trace, tracepoint_save_command, + "Save current tracepoint definitions as a script.\n" + "Use the SOURCE command in another debug session to restore them."); + + add_com ("tlimit", class_trace, trace_limit_command, + "Not sure what this should do yet...."); + + add_com ("tbuffer", class_trace, trace_buff_command, + "See also 'set trace buffer overflow'."); + + add_prefix_cmd ("tfind", class_trace, + trace_find_command, + "Select a trace frame (default by frame number).", + &tfindlist, "tfind ", 1, &cmdlist); + + add_cmd ("outside", class_trace, trace_find_outside_command, + "Select a trace frame by falling outside of a PC range", + &tfindlist); + + add_cmd ("range", class_trace, trace_find_range_command, + "Select a trace frame by PC range", &tfindlist); + + add_cmd ("tdp", class_trace, trace_find_tdp_command, + "Select a trace frame by TDP", &tfindlist); + + add_cmd ("pc", class_trace, trace_find_pc_command, + "Select a trace frame by PC", &tfindlist); + + add_com ("tdisplay", class_trace, trace_display_command, + "Display the results of a trace"); + + add_com ("tstatus", class_trace, trace_status_command, + "Inquire about trace data collection status."); + + add_com ("tstop", class_trace, trace_stop_command, + "Stop trace data collection."); + + add_com ("tstart", class_trace, trace_start_command, + "Start trace data collection."); + + add_com ("passcount", class_trace, trace_pass_command, + "Set the passcount for a tracepoint.\n" + "The trace will end when the tracepoint has been passed " + "'count' times.\n" + "Usage: passcount COUNT TPNUM, where TPNUM may also be \"all\",\n" + "or if omitted refers to the last tracepoint defined."); + + add_com ("actions", class_trace, trace_actions_command, + "Specify the actions to be taken at a tracepoint.\n" + "Actions can include collection of data, enabling or \n" + "disabling other tracepoints, or ending the trace."); + + add_cmd ("tracepoints", class_trace, delete_trace_command, + "Delete specified tracepoints; or with no argument, delete all.", + &deletelist); + + add_cmd ("tracepoints", class_trace, disable_trace_command, + "Disable specified tracepoints; or with no argument, disable all.", + &disablelist); + + add_cmd ("tracepoints", class_trace, enable_trace_command, + "Enable specified tracepoints; or with no argument, enable all.", + &enablelist); + + add_com ("trace", class_trace, trace_command, + "Set a tracepoint at a specified line, function, or address.\n" + "Argument may be a line number, function name, or " + "'*' plus an address.\n" + "For a line number or function, trace at the start of its code.\n" + "If an address is specified, trace at that exact address."); + + add_com_alias ("tp", "trace", class_alias, 0); + add_com_alias ("tr", "trace", class_alias, 1); + add_com_alias ("tra", "trace", class_alias, 1); + add_com_alias ("trac", "trace", class_alias, 1); + + +#else /* command set based on TRACE as a prefix (incomplete) */ + add_cmd ("tracepoints", class_trace, NO_FUNCTION, + "Tracing program execution without stopping the program.", + &cmdlist); + + add_prefix_cmd ("trace", class_trace, trace_command, + "prefix for tracing commands", + &tracelist, "trace ", 1, &cmdlist); + + add_cmd ("limit", class_trace, trace_limit_command, + "Not sure what the hell this does....", &tracelist); + + add_cmd ("buffer", class_trace, trace_buff_command, + "See also 'set trace buffer overflow'.", &tracelist); + + add_prefix_cmd ("find", class_trace, trace_find_command, + "Select a trace frame (default by frame number).", + &tfindlist, "trace find ", 1, &tracelist); + + add_cmd ("outside", class_trace, trace_find_outside_command, + "Select a tracepoint by falling outside of a PC range.", + &tfindlist); + + add_cmd ("range", class_trace, trace_find_range_command, + "Select a tracepoint by PC range.", + &tfindlist); + + add_cmd ("pc", class_trace, trace_find_pc_command, + "Select a tracepoint by PC.", + &tfindlist); + + add_cmd ("display", class_trace, trace_display_command, + "Display the results of a trace.", &tracelist); + + add_cmd ("delete", class_trace, delete_trace_command, + "Delete some tracepoints; no argument means all.", &tracelist); + + add_cmd ("disable", class_trace, disable_trace_command, + "Disable some tracepoints; no argument means all.", &tracelist); + + add_cmd ("enable", class_trace, enable_trace_command, + "Enable some tracepoints; no argument means all.", &tracelist); + + add_cmd ("tracepoints", class_trace, delete_trace_command, + "Delete some tracepoints; no argument means all.", + &deletelist); + + add_cmd ("tracepoints", class_trace, disable_trace_command, + "Disable some tracepoints; no argument means all.", + &disablelist); + + add_cmd ("tracepoints", class_trace, enable_trace_command, + "Enable some tracepoints; no argument means all.", + &enablelist); + + add_cmd ("at", class_trace, trace_command, + "Set a tracepoint at a specified line or function.\n" + "Argument may be a line number, function name, or " + "'*' and an address.\n" + "For a line number or function, trace from the start of " + "its code.\n" + "If an address is specified, trace at that exact address.\n" + "With no arg, uses current execution address of " + "selected stack frame.\n" + "This is useful for breaking on return to a stack frame.", + &tracelist); + + add_cmd ("status", class_trace, trace_status_command, + "Inquire about trace data collection status.", &tracelist); + + add_cmd ("stop", class_trace, trace_stop_command, + "Stop trace data collection.", &tracelist); + + add_cmd ("start", class_trace, trace_start_command, + "Start trace data collection.", &tracelist); + + add_cmd ("info", class_info, tracepoints_info, + "Status of tracepoints, or tracepoint number NUMBER.\n" + "The \"Address\" and \"What\" columns indicate the\n" + "address and file/line number respectively.\n\n" + "Convenience variable \"$tpnum\" contains the number of the\n" + "last tracepoint set.", + &tracelist); + + add_info ("tracepoints", tracepoints_info, + "Status of tracepoints, or tracepoint number NUMBER.\n" + "The \"Address\" and \"What\" columns indicate the\n" + "address and file/line number respectively.\n\n" + "Convenience variable \"$tpnum\" contains the number of the\n" + "last tracepoint set."); + + add_info_alias ("tp", "tracepoints", 1); + +#endif +} + diff --git a/gdb/tracepoint.h b/gdb/tracepoint.h new file mode 100644 index 0000000000..fa6f4eb520 --- /dev/null +++ b/gdb/tracepoint.h @@ -0,0 +1,97 @@ +/* Data structures associated with tracepoints in GDB. + Copyright (C) 1997 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 2 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, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if !defined (TRACEPOINT_H) +#define TRACEPOINT_H 1 + +enum enabled { disabled, enabled }; + +/* The data structure for an action: */ +struct action_line +{ + struct action_line *next; + char *action; +}; + +/* The data structure for a tracepoint: */ + +struct tracepoint +{ + struct tracepoint *next; + + enum enabled enabled; + +#if 0 + /* Type of tracepoint (MVS FIXME: needed?). */ + enum tptype type; + + /* What to do with this tracepoint after we hit it MVS FIXME: needed?). */ + enum tpdisp disposition; +#endif + /* Number assigned to distinguish tracepoints. */ + int number; + + /* Address to trace at, or NULL if not an instruction tracepoint (MVS ?). */ + CORE_ADDR address; + + /* Line number of this address. Only matters if address is non-NULL. */ + int line_number; + + /* Source file name of this address. Only matters if address is non-NULL. */ + char *source_file; + + /* Number of times this tracepoint should single-step + and collect additional data */ + long step_count; + + /* Number of times this tracepoint should be hit before disabling/ending. */ + int pass_count; + + /* Chain of action lines to execute when this tracepoint is hit. */ + struct action_line *actions; + + /* Conditional (MVS ?). */ + struct expression *cond; + + /* String we used to set the tracepoint (malloc'd). Only matters if + address is non-NULL. */ + char *addr_string; + + /* Language we used to set the tracepoint. */ + enum language language; + + /* Input radix we used to set the tracepoint. */ + int input_radix; + + /* String form of the tracepoint trigger condition (malloc'd), + or NULL if there is no condition. */ + char *cond_string; + + /* Count of the number of times this tracepoint was taken, dumped + with the info, but not used for anything else. Useful for + seeing how many times you hit a tracepoint prior to the program + aborting, so you can back up to just before the abort. */ + int hit_count; + + /* Thread number for thread-specific breakpoint, or -1 if don't care */ + int thread; +}; + + +#endif /* TRACEPOINT_H */