/* Target-dependent code for the or1k architecture, for GDB, the GNU Debugger. Copyright 1988-1999, Free Software Foundation, Inc. Contributed by Alessandro Forin(af@cs.cmu.edu at CMU and by Per Bothner(bothner@cs.wisc.edu) at U.Wisconsin. 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 #include "demangle.h" #include "defs.h" #include "gdb_string.h" #include "frame.h" #include "inferior.h" #include "symtab.h" #include "value.h" #include "gdbcmd.h" #include "language.h" #include "gdbcore.h" #include "symfile.h" #include "objfiles.h" #include "gdbtypes.h" #include "target.h" #include "regcache.h" #include "osabi.h" #include "arch-utils.h" #include "opcode/or32.h" /* *INDENT-OFF* */ static const char *compare_to_names[NUM_CT_NAMES] = { "DISABLED", "FETCH", "LEA", "SEA", "LDATA", "SDATA", "AEA", "ADATA" }; static const char *or1k_record_names[MAX_RECORD_NAMES] = { "PC", "LSEA", "LDATA", "SDATA", "READSPR", "WRITESPR", "INSTR" }; static const char *or1k_is_names[MAX_IS_NAMES] = { "IF_NONE", "IF_NORMAL", "IF_BRANCH", "IF_DELAY" }; static const char *or1k_ls_names[MAX_LS_NAMES] = { "LS_NONE", " ", "LBZ", "LBS", "LHZ", "LHS", "LWZ", "LWS", " ", " ", "SB", " ", "SH", " ", "SW", " " }; /* *INDENT-ON* */ /* The list of available "set or1k " and "show or1k " commands */ static struct cmd_list_element *htrace_cmdlist = NULL; static struct cmd_list_element *htrace_mode_cmdlist = NULL; struct htrace_struct or1k_htrace; struct hwatch_struct or1k_hwatch[MAX_HW_WATCHES]; int num_hw_watches = 0; /* Trace data. */ char trace_filename[TRACE_FILENAME_SIZE] = "trace.dat"; /* Previous values for buffer display. */ static int prev_length = 10; static int prev_from = 0; /* Size of trace file in records. */ int trace_size = 0; static union exp_element exp_error; /* Parses compare variable and returns it into ct. */ union exp_element *or1k_parse_ct (union exp_element *exp, int *ct) { int i; if (exp->opcode != OP_INTERNALVAR) error ("Valid lvalue expected."); exp++; for (i = 1; i < NUM_CT_NAMES; i++) if (strcasecmp (compare_to_names[i], exp->internalvar->name) == 0) break; if (i >= NUM_CT_NAMES) error ("Invalid compare to operand."); *ct = i; exp++; if (exp->opcode != OP_INTERNALVAR) return &exp_error; exp++; return exp; } /* Parses compare value and returns it into cv. */ union exp_element *or1k_parse_cv (union exp_element *exp, unsigned int *cv) { switch (exp->opcode) { case UNOP_IND: exp++; exp = or1k_parse_cv (exp, cv); *cv = or1k_fetch_word (*cv); break; case OP_LONG: exp += 2; *cv = exp->longconst; exp += 2; break; case OP_REGISTER: exp++; *cv = read_register (exp->longconst); exp += 2; break; default: error ("Value expected."); } return exp; } /* Parse conditional. Puts freshly allocated array of matchpoints into match. */ union exp_element * or1k_parse_cond (union exp_element *exp, struct matchpoint **match, int *nmatch) { unsigned int ct; *match = (struct matchpoint *) malloc (sizeof (struct matchpoint)); *nmatch = 1; switch (exp->opcode) { case BINOP_EQUAL: (*match)->dcr.cc = CC_EQUAL; break; case BINOP_NOTEQUAL: (*match)->dcr.cc = CC_NEQUAL; break; case BINOP_LESS: (*match)->dcr.cc = CC_LESS; break; case BINOP_GTR: (*match)->dcr.cc = CC_GREAT; break; case BINOP_LEQ: (*match)->dcr.cc = CC_LESSE; break; case BINOP_GEQ: (*match)->dcr.cc = CC_GREATE; break; case BINOP_BITWISE_AND: (*match)->dcr.cc = CC_MASKED; break; default: return &exp_error; } exp++; (*match)->dcr.dp = 1; (*match)->dcr.sc = 0; if (exp->opcode == OP_INTERNALVAR) { exp = or1k_parse_ct (exp, &ct); exp = or1k_parse_cv (exp, &(*match)->dvr); } else { exp = or1k_parse_cv (exp, &(*match)->dvr); exp = or1k_parse_ct (exp, &ct); } (*match)->dcr.ct = ct; (*match)->chain_type = CHAINING_NONE; (*match)->cause_breakpoint = 0; return exp; } /* Parses expression with && or || operators. Puts freshly allocated array of matchpoints into match. valid & 1: && is allowed, valid & 2: || is allowed. */ union exp_element * or1k_parse_any (union exp_element *exp, struct matchpoint **match, int *nmatch, int valid) { union exp_element *tmp; int first_and_only = 0, first_or_only = 0; struct matchpoint *tmp_match1, *tmp_match2, *tmpm; int tmp_nmatch1, tmp_nmatch2, tmpn; switch (exp->opcode) { case BINOP_LOGICAL_AND: if (!(valid & 1)) return &exp_error; exp++; /* Parse first argument. */ tmp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, 1); if (tmp == &exp_error) exp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, valid); else { /* and_only successful */ exp = tmp; first_and_only = 1; } if (exp == &exp_error) return &exp_error; /* Parse second argument. */ if (first_and_only) exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, valid); else exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, 1); if (exp == &exp_error) return &exp_error; if (first_and_only) { /* Exchange structures, so that and_only is listed last. */ struct matchpoint *tmpm = tmp_match1; int tmpn = tmp_nmatch1; tmp_match1 = tmp_match2; tmp_nmatch1 = tmp_nmatch2; tmp_match2 = tmpm; tmp_nmatch2 = tmpn; } *nmatch = tmp_nmatch1 + tmp_nmatch2; *match = (struct matchpoint *)malloc (*nmatch * sizeof (struct matchpoint)); memcpy (*match, tmp_match1, tmp_nmatch1 * sizeof (struct matchpoint)); free (tmp_match1); tmp_match2[0].chain_type = CHAINING_AND; memcpy (*match + tmp_nmatch1, tmp_match2, tmp_nmatch2 * sizeof (struct matchpoint)); free (tmp_match2); return exp; case BINOP_LOGICAL_OR: if (!(valid & 2)) return &exp_error; exp++; /* Parse first argument. */ tmp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, 2); if (tmp == &exp_error) exp = or1k_parse_any (exp, &tmp_match1, &tmp_nmatch1, valid); else { /* and_only successful */ exp = tmp; first_or_only = 1; } if (exp == &exp_error) return &exp_error; /* Parse second argument. */ if (first_or_only) exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, valid); else exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, 2); if (exp == &exp_error) return &exp_error; if (first_or_only) { /* Exchange structures, so that and_only is listed first. */ struct matchpoint *tmpm = tmp_match1; int tmpn = tmp_nmatch1; tmp_match1 = tmp_match2; tmp_nmatch1 = tmp_nmatch2; tmp_match2 = tmpm; tmp_nmatch2 = tmpn; } *nmatch = tmp_nmatch1 + tmp_nmatch2; *match = (struct matchpoint *)malloc (*nmatch * sizeof (struct matchpoint)); memcpy (*match, tmp_match1, tmp_nmatch1 * sizeof (struct matchpoint)); free (tmp_match1); tmp_match2[0].chain_type = CHAINING_OR; memcpy (*match + tmp_nmatch1, tmp_match2, tmp_nmatch2 * sizeof (struct matchpoint)); free (tmp_match2); return exp; default: return or1k_parse_cond (exp, match, nmatch); } } /* Parses sequence of ||s. Puts freshly allocated array of matchpoints into match. */ union exp_element * or1k_parse_or (union exp_element *exp, struct matchpoint **match, int *nmatch, int set_break) { struct matchpoint *tmp_match1, *tmp_match2; int tmp_nmatch1, tmp_nmatch2; switch (exp->opcode) { case BINOP_LOGICAL_OR: exp++; exp = or1k_parse_or (exp, &tmp_match1, &tmp_nmatch1, set_break); if (exp == &exp_error) return &exp_error; exp = or1k_parse_any (exp, &tmp_match2, &tmp_nmatch2, 3); if (set_break) { tmp_match1[tmp_nmatch1 - 1].cause_breakpoint = 1; tmp_match2[tmp_nmatch2 - 1].cause_breakpoint = 1; } *nmatch = tmp_nmatch1 + tmp_nmatch2; *match = (struct matchpoint *)malloc (*nmatch * sizeof (struct matchpoint)); memcpy (*match, tmp_match1, tmp_nmatch1 * sizeof (struct matchpoint)); free (tmp_match1); memcpy (*match + tmp_nmatch1, tmp_match2, tmp_nmatch2 * sizeof (struct matchpoint)); free (tmp_match2); return exp; default: return or1k_parse_any (exp, match, nmatch, 3); if (set_break) (*match)[*nmatch - 1].cause_breakpoint = 1; } } /* Prints single matchpoint from specified struct. */ static void print_matchpoint_struct (struct matchpoint *mp) { printf_filtered ("%-6s (%i) %u, ON=%i, chain_type=%i, cause break=%i\n", compare_to_names[mp->dcr.ct], mp->dcr.cc, mp->dvr, mp->dcr.dp, mp->chain_type, mp->cause_breakpoint); } /* Build watchpoint (s) based on given structure. */ static void set_matchpoints (struct matchpoint *match, int nmatch) { int i; debug_regs_changed = 1; sift_matchpoints (); for (i = 0; i < nmatch; i++) { int num = or1k_implementation.num_used_matchpoints; dcr[num] = match[i].dcr; dvr[num] = match[i].dvr; /* Set chaining bits. */ dmr1 &= ~(3 << (2 * num)); dmr1 |= match[i].chain_type << (2 * num); /* Set watchpoint bits */ dmr2 &= 1 << num; dmr2 |= match[i].cause_breakpoint << num; matchpoint_user_count[i]++; or1k_implementation.num_used_matchpoints++; } } /* Returns nonzero, if matchpoints [start .. start+nmatch-1] are equal to match record. */ static int matchpoint_matches (int start, struct matchpoint *match, int nmatch) { int i; if (nmatch + start >= or1k_implementation.num_matchpoints) return 0; for (i = 0; i < nmatch; i++) { int j = i + start; /* Everything exept cause breakpoint must match. */ if (dcr[j].dp != match[i].dcr.dp || dcr[j].ct != match[i].dcr.ct || dcr[j].cc != match[i].dcr.cc || dcr[j].sc != match[i].dcr.sc || dvr[j] != match[i].dvr || match[i].chain_type != ((dmr1 >> (2 * j)) & 3)) return 0; } return 1; } static void hwatch_command (char *arg, int from_tty) { struct expression *exp; int i, nfree, nmatch, remove = 0; struct matchpoint *match; if (arg == NULL) arg = ""; if (strncasecmp ("remove ", arg, 7) == 0) { arg += 7; remove = 1; } /* Parse arguments. */ exp = parse_exp_1 (&arg, 0, 0); #ifdef DEBUG dump_prefix_expression (exp, gdb_stdout, "expr1"); #endif if (or1k_parse_or (&exp->elts[0], &match, &nmatch, 1) == &exp_error) error ("Watchpoint too complex."); for (i = 0; i < nmatch; i++) print_matchpoint_struct (&match[i]); if (remove) { int start = -1; int cleared = 0; if (num_hw_watches <= 0) error ("No extended hardware supported watchpoints present."); for (i = 0; i < num_hw_watches; i++) if (matchpoint_matches (or1k_hwatch[i].matchpoint_start, match, nmatch)) { start = or1k_hwatch[i].matchpoint_start; break; } if (start < 0) error ("Watchpoint not found."); for (i = 0; i < nmatch; i++) { int j = start + i; if (--matchpoint_user_count[j] <= 0) { debug_regs_changed = 1; memset (&dcr[j], 0, sizeof (dcr[j])); or1k_implementation.num_used_matchpoints--; cleared = 1; } } if (!cleared) warning ("No matchpoint(s) freed. Resources are busy."); } else { if (num_hw_watches >= MAX_HW_WATCHES) error ("Number of watchpoints too large."); /* Now we have to find out if given prefix expression matches our HW based support. It may take up to or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints. */ nfree = or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints; if (nmatch > nfree) error ("Not enough free matchpoint resources."); /* Build watchpoint(s) based on just built structure. */ or1k_hwatch[num_hw_watches].matchpoint_start = or1k_implementation.num_used_matchpoints; set_matchpoints (match, nmatch); num_hw_watches++; printf_unfiltered ("Watchpoint successfully allocated.\n"); } free (match); free (exp); } static void htrace_command (char *args, int from_tty) { help_list (htrace_cmdlist, "htrace ", all_commands, gdb_stdout); } static void htrace_mode_command (char *args, int from_tty) { help_list (htrace_mode_cmdlist, "htrace mode ", all_commands, gdb_stdout); } static void htrace_mode_contin_command (char *args, int from_tty) { or1k_htrace.moder.contin = 1; printf_unfiltered ("Continuous trace mode set.\n"); } static void htrace_mode_suspend_command (char *args, int from_tty) { or1k_htrace.moder.contin = 0; printf_unfiltered ("Suspend trace mode set.\n"); } static void print_event_struct (struct htrace_event_struct *event, int stop) { int i; if (event->operation == TRIGOP_ANY) if (stop) printf_filtered ("not active"); else printf_filtered ("always active"); else { char *comma; if (event->operation == TRIGOP_AND) comma = "&("; else comma = "|("; if (event->is_valid) { printf_filtered ("%s%s", comma, or1k_is_names[event->is_trig]); comma = ", "; } if (event->ls_valid) { printf_filtered ("%s%s", comma, or1k_ls_names[event->ls_trig]); comma = ", "; } if (event->bp_valid) { printf_filtered ("%sbreak", comma); comma = ", "; } if (event->wp_valid) for (i = 0; i < 11; i++) if ((event->wp_trig >> i) & 1) { printf_filtered ("%sWP%i", comma, i); comma = ", "; } if (comma[0] == ',') printf_filtered (")"); else printf_filtered ("not active"); } } static void print_record_struct (struct htrace_record_struct *record) { int i; char *comma = ""; for (i = 0; i < MAX_RECORD_NAMES; i++) { if ((record->rec >> i)&1) { printf_filtered ("%s%s", comma, or1k_record_names[i]); comma = ", "; } } if (!*comma) printf_unfiltered ("none"); } static void htrace_info_command (char *args, int from_tty) { int i; printf_filtered ("Trace trigger: "); print_event_struct (&or1k_htrace.trig, 0); printf_filtered ("\nTrace qualifier: "); print_event_struct (&or1k_htrace.qual, 0); for (i = 0; i < MAX_MATCHPOINTS; i++) { printf_filtered ("\n WP%i records: ", i); print_record_struct (&or1k_htrace.recwp[i]); } printf_filtered ("\n BP records: "); print_record_struct (&or1k_htrace.recbp); printf_filtered ("\nTrace stop: "); print_event_struct (&or1k_htrace.stop, 1); printf_filtered ("\n"); } /* Parses event from given string. Result is placed into event structure, and previously allocated resources are freed. Parameter stop is nonzero, when we are parsing for stop criteria. */ static void parse_event (char *args, struct htrace_event_struct *event, int stop) { int i, op_type = 0, was_last_op = 1, any = 0; /* Release previous resources. */ for (i = 0; i < MAX_MATCHPOINTS; i++) { if ((event->wp_trig << i) & 1) if (--matchpoint_user_count[i] <= 0) { memset (&dcr[i], 0, sizeof (dcr[i])); debug_regs_changed = 1; or1k_implementation.num_used_matchpoints--; } } event->is_valid = event->is_trig = 0; event->ls_valid = event->ls_trig = 0; event->bp_valid = event->bp_trig = 0; event->wp_valid = event->wp_trig = 0; if (args == NULL) args = ""; while (*args == ' ') args++; while (*args != '\0') { if (strncasecmp ("breakpoint", args, 10) == 0) { if (!was_last_op) error ("Syntax error."); was_last_op = 0; event->bp_valid = event->bp_trig = 1; } else if ((!stop && strncasecmp ("any", args, 3) == 0) || (stop && strncasecmp ("none", args, 4) == 0)) { if (!was_last_op) error ("Syntax error."); was_last_op = 0; any = 1; } else if (strncasecmp ("||", args, 2) == 0) { if (op_type == TRIGOP_AND) error ("Only one type of logical operator allowed at a time."); op_type = TRIGOP_OR; if (was_last_op) error ("Syntax error."); was_last_op = 1; args += 2; } else if (strncasecmp ("&&", args, 2) == 0) { if (op_type == TRIGOP_OR) error ("Only one type of logical operator allowed at a time."); op_type = TRIGOP_AND; if (was_last_op) error ("Syntax error."); was_last_op = 1; args += 2; } else { int found = 0; if (!was_last_op) error ("Syntax error."); was_last_op = 0; /* Search through is and ls tables for a match. */ for (i = 0; i < MAX_IS_NAMES; i++) if (strncasecmp (args, or1k_is_names[i], strlen (or1k_is_names[i])) == 0) { event->is_valid = 1; event->is_trig = i; args += strlen (or1k_is_names[i]); found = 1; break; } if (!found) { for (i = 0; i < MAX_LS_NAMES; i++) if (strncasecmp (args, or1k_ls_names[i], strlen (or1k_ls_names[i])) == 0) { event->ls_valid = 1; event->ls_trig = i; args += strlen (or1k_ls_names[i]); found = 1; break; } } if (!found) { /* No special name was found => parse expression. */ struct expression *exp; struct matchpoint *match; int nmatch, nfree; exp = parse_exp_1 (&args, 0, 0); if (or1k_parse_any (&exp->elts[0], &match, &nmatch, 3) == &exp_error) error ("Expression too complex."); for (i = 0; i < nmatch; i++) print_matchpoint_struct (&match[i]); /* Now we have to find out if given prefix expression matches our HW based support. It may take up to or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints. */ nfree = or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints; if (nmatch > nfree) error ("Not enough free matchpoint resources."); /* Build matchpoint(s) based on just built structure. */ set_matchpoints (match, nmatch); event->wp_valid = 1; event->wp_trig |= 1 << (or1k_implementation.num_used_matchpoints - 1); printf_unfiltered ("Watchpoint successfully allocated.\n"); free (match); free (exp); found = 1; } if (!found) warning ("Invalid event at '%s'", args); } while (*args == ' ') args++; } if (any) event->operation = TRIGOP_ANY; else { if (op_type == 0) op_type = TRIGOP_AND; event->operation = op_type; } } static void htrace_trigger_command (char *args, int from_tty) { parse_event (args, &or1k_htrace.trig, 0); printf_filtered ("Trace starts, when:\n"); print_event_struct (&or1k_htrace.trig, 0); printf_filtered ("\n"); } static void htrace_qualifier_command (char *args, int from_tty) { parse_event (args, &or1k_htrace.qual, 0); printf_filtered ("Trace records, when:\n"); print_event_struct (&or1k_htrace.qual, 0); printf_filtered ("\n"); } static void htrace_stop_command (char *args, int from_tty) { parse_event (args, &or1k_htrace.stop, 1); printf_filtered ("Trace stops, when:\n"); print_event_struct (&or1k_htrace.stop, 1); printf_filtered ("\n"); } static void htrace_clear_records_command (char *args, int from_tty) { int i, j, cleared = 0; /* Clear all. */ for (i = 0; i < MAX_MATCHPOINTS; i++) { for (j = 0; j < MAX_MATCHPOINTS; j++) { if ((or1k_htrace.wp_record_uses[i] << j) & 1) if (--matchpoint_user_count[j] <= 0) { memset (&dcr[j], 0, sizeof (dcr[j])); debug_regs_changed = 1; cleared = 1; or1k_implementation.num_used_matchpoints--; } } or1k_htrace.wp_record_uses[i] = 0; } if (!cleared) warning ("No matchpoints freed. Resources are busy."); } /* Syntax: htrace record {data}* when {expr} */ static void htrace_record_command (char *args, int from_tty) { struct expression *exp; int i, nfree, nmatch, wp = -1; struct matchpoint *match; unsigned int recdata = 0; char *c; if (args == '\0') error ( "Please specify data to record, e.g.:\n" "htrace record PC SDATA when $SEA == 100\n" "htrace record when $SEA == 100 to remove record"); for (i = 0; *args != '\0' && strncasecmp ("when ", args, 5); i++) { int j, found = 0; for (j = 0; j < MAX_RECORD_NAMES; j++) if (strncasecmp (args, or1k_record_names[j], strlen (or1k_record_names[j])) == 0) { recdata |= 1 << j; found = 1; break; } if (!found) warning ("Invalid record data name at '%s'.", args); while (*args != ' ' && *args != '\0') args++; while (*args == ' ') args++; } if (strncasecmp ("when ", args, 5) != 0) if (*args == '\0') { warning ("Condition not set. Assuming breakpoint."); wp = -1; } else error ("Syntax error."); else { args += 5; if (strcasecmp ("breakpoint", args) == 0) wp = -1; else { /* Parse arguments. */ exp = parse_exp_1 (&args, 0, 0); #ifdef DEBUG dump_prefix_expression (exp, gdb_stdout, "expr1"); #endif if (or1k_parse_any (&exp->elts[0], &match, &nmatch, 3) == &exp_error) error ("Expression too complex."); for (i = 0; i < nmatch; i++) print_matchpoint_struct (&match[i]); if (recdata) { /* Now we have to find out if given prefix expression matches our HW based support. It may take up to or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints. */ nfree = or1k_implementation.num_matchpoints - or1k_implementation.num_used_matchpoints; if (nmatch > nfree) error ("Not enough free matchpoint resources."); wp = or1k_implementation.num_used_matchpoints - 1; or1k_htrace.wp_record_uses[wp] = 0; for (i = or1k_implementation.num_used_matchpoints; i <= wp; i++) or1k_htrace.wp_record_uses[wp] |= 1 << i; set_matchpoints (match, nmatch); } else { /* Remove record. */ int start = -1, cleared = 0; for (i = 0; i < MAX_MATCHPOINTS; i++) { int mp_start = 0, j; j = or1k_htrace.wp_record_uses[i]; while (j > 0 && (j & 1) == 0) mp_start++; if (matchpoint_matches (mp_start, match, nmatch)) { start = mp_start; or1k_htrace.wp_record_uses[i] = 0; break; } } if (start < 0) error ("Record with such expression not found."); for (i = 0; i < nmatch; i++) { int j = i + start; if (--matchpoint_user_count[j] <= 0) { memset (&dcr[j], 0, sizeof (dcr[j])); debug_regs_changed = 1; cleared = 1; } } if (!cleared) warning ("No matchpoint(s) freed."); } } } /* If we reached this point we have matchpoints set, and wp holds the value of that watchpoint. wp == -1, if breakpoint was specified. */ if (wp < 0) or1k_htrace.recbp.rec = recdata; else or1k_htrace.recwp[wp].rec = recdata; if (recdata) { printf_unfiltered ("Data"); for (i = 0; i < MAX_RECORD_NAMES; i++) if ((recdata >> i) & 1) printf_unfiltered (" %s,", or1k_record_names[i]); } else printf_unfiltered ("No data"); if (wp < 0) printf_unfiltered (" will be recorded when breakpoint occurs\n"); else printf_unfiltered (" will be recorded when watchpoint #%i occurs\n", wp); } static void htrace_enable_command (char *args, int from_tty) { or1k_htrace.moder.trace_enable = 1; printf_unfiltered ("HW Trace enabled.\n"); } static void htrace_disable_command (char *args, int from_tty) { or1k_htrace.moder.trace_enable = 0; printf_unfiltered ("HW Trace disabled.\n"); } static void htrace_rewind_command (char *args, int from_tty) { FILE *f; if (args != NULL && *args != '\0') strncpy (TRACE_FILENAME, args, TRACE_FILENAME_SIZE); /* Just empty it. */ if ((f = fopen (TRACE_FILENAME, "wb+")) == NULL) error ("Cannot open trace file."); fclose (f); printf_unfiltered ("Trace data cleared.\n"); } static void print_data_struct (unsigned int pos, struct htrace_data_struct *data) { struct symbol *func; char *funname = NULL; if (data->type < 4) { /* Determine function name - copied from stack.c */ func = find_pc_function (data->data); if (func) { struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (data->data); if (msymbol != NULL && (SYMBOL_VALUE_ADDRESS (msymbol) > BLOCK_START (SYMBOL_BLOCK_VALUE (func)))) funname = SYMBOL_NAME (msymbol); else { char *demangled; funname = SYMBOL_NAME (func); if (SYMBOL_LANGUAGE (func) == language_cplus) { demangled = cplus_demangle (funname, DMGL_ANSI); if (demangled == NULL) funname = SYMBOL_SOURCE_NAME (func); } } } else { struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (data->data); if (msymbol != NULL) funname = SYMBOL_NAME (msymbol); } } printf_filtered ("%06X%c %-8s %08X", pos, data->valid ? '>' : ':', or1k_record_names[data->type], data->data); if (funname) printf_filtered (" (%s)\n", funname); else printf_filtered ("\n"); } /* Prints out trace buffer. */ static void htrace_print_command (char *args, int from_tty) { int i, from = 0, length = prev_length; FILE *f; struct htrace_data_struct *td; if (args == NULL) args = ""; while (*args == ' ') args++; if (*args == '\0') { /* We will display buffer further. */ from = prev_from + prev_length; } else { /* Display buffer range. */ int numbers = 0; char *cnum = args; while (*args != ' ' && *args != '\0') args++; /* Any arguments? */ if (*args == '\0') numbers = 1; else { *args = 0; args++; numbers = 2; } from = strtoul (cnum, &cnum, 0); if (*cnum != 0) error ("Invalid from value."); if (from < 0) from += trace_size; if (numbers == 2) { while (*args == ' ') args++; length = strtoul (cnum, &cnum, 0); if (*args != 0) error ("Invalid length value."); if (length < 0) { from += length; length = -length; } } } if (from >= trace_size) from = trace_size - 1; if (from < 0) from = 0; if (from + length >= trace_size) length = trace_size - from; prev_length = length; prev_from = from; if (length == 0) error ("Nothing to print."); printf_filtered ("Trace buffer %06x:%06x (size = %i)\n", from, from + length - 1, length); if ((f = fopen (TRACE_FILENAME, "rb")) == NULL) error ("Cannot open trace file."); if (fseek (f, TRACE_DATA_SIZE * from, SEEK_SET)) error ("Error reading trace file."); td = (struct htrace_data_struct *) malloc (TRACE_DATA_SIZE * length); length = fread (td, TRACE_DATA_SIZE, length, f); for (i = 0; i < length; i++) print_data_struct (from + i, &td[i]); fclose (f); } /* Adds a group of hwatch commands to gdb */ void add_htrace_commands () { /* hwatch command. */ add_com ("hwatch", class_breakpoint, hwatch_command, "Set hardware watch" "point.\nExample: ($LEA == my_var)&&($LDATA < 50)||($SEA == my_" "var)&&($SDATA >= 50).\nSee OR1k Architecture document for more" " info."); /* htrace commands. */ add_prefix_cmd ("htrace", class_breakpoint, htrace_command, "Group of commands for handling hardware assisted trace\n\n" "See OR1k Architecture and gdb for or1k documents for more info.", &htrace_cmdlist, "htrace ", 0, &cmdlist); add_cmd ("info", class_breakpoint, htrace_info_command, "Display information about HW trace.", &htrace_cmdlist); add_alias_cmd ("i", "info", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("trigger", class_breakpoint, htrace_trigger_command, "Set starting criteria for trace.", &htrace_cmdlist); add_alias_cmd ("t", "trigger", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("qualifier", class_breakpoint, htrace_qualifier_command, "Set acquisition qualifier for HW trace.", &htrace_cmdlist); add_alias_cmd ("q", "qualifier", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("stop", class_breakpoint, htrace_stop_command, "Set HW trace stopping criteria.", &htrace_cmdlist); add_alias_cmd ("s", "stop", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("record", class_breakpoint, htrace_record_command, "Sets data to be recorded when expression occurs.", &htrace_cmdlist); add_alias_cmd ("r", "record", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("clear records", class_breakpoint, htrace_clear_records_command, "Disposes all matchpoints used by records.", &htrace_cmdlist); add_cmd ("enable", class_breakpoint, htrace_enable_command, "Enables the HW trace.", &htrace_cmdlist); add_alias_cmd ("e", "enable", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("disable", class_breakpoint, htrace_disable_command, "Disables the HW trace.", &htrace_cmdlist); add_alias_cmd ("d", "disable", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("rewind", class_breakpoint, htrace_rewind_command, "Clears currently recorded trace data.\n" "If filename is specified, new trace file is made and any newly collected data\n" "will be written there.", &htrace_cmdlist); add_cmd ("print", class_breakpoint, htrace_print_command, "Prints trace buffer, using current record configuration.\n" "htrace print [ []]\n" "htrace print" , &htrace_cmdlist); add_alias_cmd ("p", "print", class_breakpoint, 1, &htrace_cmdlist); add_prefix_cmd ("mode", class_breakpoint, htrace_mode_command, "Configures the HW trace.\n" "htrace mode [continuous|suspend]" , &htrace_mode_cmdlist, "htrace mode ", 0, &htrace_cmdlist); add_alias_cmd ("m", "mode", class_breakpoint, 1, &htrace_cmdlist); add_cmd ("continuous", class_breakpoint, htrace_mode_contin_command, "Set continuous trace mode.\n", &htrace_mode_cmdlist); add_cmd ("suspend", class_breakpoint, htrace_mode_suspend_command, "Set suspend trace mode.\n", &htrace_mode_cmdlist); }