2002-08-14 Andrew Cagney * Makefile.in (complaints.o): Update dependencies. * symfile.c (syms_from_objfile): Add symfile_complaints parameter to call to clear_complaints. (new_symfile_objfile, reread_symbols): Ditto. (oldsyms_complaint): Delete. (empty_symtab_complaint, unknown_option_complaint): Delete. (free_named_symtabs): Use complaint instead of complain. * complaints.h: Update copyright. (struct complaints): Declare. (struct complaint): Make `message' constant. (complaint): Declare. (complaint_root): Delete declaration. (symfile_complaints): Delete declaration. (struct complaints): Add opaque declaration. (clear_complaints): Add a complaints parameter. * complaints.c: Update copyright. (enum complaint_series): Define. (complaint_root): Delete. (struct complaints): Define. (complaint_sentinel, symfile_complaint_book): New variables. (symfile_explanations, symfile_complaints): New variables. New variables. (get_complaints): New function. (vcomplain): New function. (complain): Call vcomplain with symfile_complaint. (complaint): New function. (clear_complaints): Rewrite. (__initialize_complaints): Use add_setshow_command. Index: testsuite/ChangeLog 2002-08-14 Andrew Cagney * gdb.gdb/complaints.exp: New file. Index: Makefile.in =================================================================== RCS file: /cvs/src/src/gdb/Makefile.in,v retrieving revision 1.236 diff -u -r1.236 Makefile.in --- Makefile.in 14 Aug 2002 18:13:30 -0000 1.236 +++ Makefile.in 15 Aug 2002 01:32:08 -0000 @@ -1382,7 +1382,8 @@ $(buildsym_h) $(gdb_stabs_h) $(stabsread_h) $(complaints_h) \ $(target_h) $(gdb_assert_h) -complaints.o: complaints.c $(complaints_h) $(defs_h) $(gdbcmd_h) +complaints.o: complaints.c $(defs_h) $(complaints_h) $(gdb_assert_h) \ + $(command_h) $(gdbcmd_h) # Provide explicit rule/dependency - works for more makes. copying.o: $(srcdir)/copying.c $(defs_h) $(gdbcmd_h) Index: complaints.c =================================================================== RCS file: /cvs/src/src/gdb/complaints.c,v retrieving revision 1.6 diff -u -r1.6 complaints.c --- complaints.c 6 Nov 2001 23:38:14 -0000 1.6 +++ complaints.c 15 Aug 2002 01:32:08 -0000 @@ -1,6 +1,7 @@ /* Support for complaint handling during symbol reading in GDB. - Copyright 1990, 1991, 1992, 1993, 1995, 1998, 1999, 2000 - Free Software Foundation, Inc. + + Copyright 1990, 1991, 1992, 1993, 1995, 1998, 1999, 2000, 2002 Free + Software Foundation, Inc. This file is part of GDB. @@ -21,54 +22,109 @@ #include "defs.h" #include "complaints.h" +#include "gdb_assert.h" +#include "command.h" #include "gdbcmd.h" extern void _initialize_complaints (void); +/* Should each complaint message be self explanatory, or should we assume that + a series of complaints is being produced? */ + +/* case 1: First message of a series that must + start off with explanation. case 2: Subsequent message of a series + that needs no explanation (the user already knows we have a problem + so we can just state our piece). */ +enum complaint_series { + /* Isolated self explanatory message. */ + ISOLATED_MESSAGE, + /* First message of a series, includes an explanation. */ + FIRST_MESSAGE, + /* First message of a series, but does not need to include any sort + of explanation. */ + SHORT_FIRST_MESSAGE, + /* Subsequent message of a series that needs no explanation (the + user already knows we have a problem so we can just state our + piece). */ + SUBSEQUENT_MESSAGE +}; + /* Structure to manage complaints about symbol file contents. */ -struct complaint complaint_root[1] = +struct complaints { - { - (char *) NULL, /* Complaint message */ - 0, /* Complaint counter */ - complaint_root /* Next complaint. */ - } + struct complaint *root; + + /* Should each complaint be self explanatory, or should we assume + that a series of complaints is being produced? case 0: Isolated + self explanatory message. case 1: First message of a series that + must start off with explanation. case 2: Subsequent message of a + series that needs no explanation (the user already knows we have + a problem so we can just state our piece). */ + int series; + + /* The explanatory messages that should accompany the complaint. + NOTE: cagney/2002-08-14: In a desperate attempt at being vaguely + i18n friendly, this is an array of two messages. When present, + EXPLANATION[SERIES] is used to wrap the message. */ + const char **explanation; }; -/* How many complaints about a particular thing should be printed before - we stop whining about it? Default is no whining at all, since so many - systems have ill-constructed symbol files. */ +static struct complaint complaint_sentinel; -static unsigned int stop_whining = 0; +/* The symbol table complaint table. */ -/* Should each complaint be self explanatory, or should we assume that - a series of complaints is being produced? - case 0: self explanatory message. - case 1: First message of a series that must start off with explanation. - case 2: Subsequent message, when user already knows we are reading - symbols and we can just state our piece. */ +static const char *symfile_explanations[] = { + "During symbol reading, %s.\n", + "During symbol reading...%s...", + "%s...", + "%s...", + NULL +}; -static int complaint_series = 0; +static struct complaints symfile_complaint_book = { + &complaint_sentinel, + 0, + symfile_explanations +}; +struct complaints *symfile_complaints = &symfile_complaint_book; - +/* Wrapper function to, on-demand, fill in a complaints object. */ -/* Functions to handle complaints during symbol reading. */ +static struct complaints * +get_complaints (struct complaints **c) +{ + if ((*c) != NULL) + return (*c); + (*c) = XMALLOC (struct complaints); + (*c)->root = &complaint_sentinel; + (*c)->series = ISOLATED_MESSAGE; + (*c)->explanation = NULL; + return (*c); +} -/* Print a complaint about the input symbols, and link the complaint block - into a chain for later handling. */ +/* How many complaints about a particular thing should be printed + before we stop whining about it? Default is no whining at all, + since so many systems have ill-constructed symbol files. */ -void -complain (struct complaint *complaint,...) -{ - va_list args; - va_start (args, complaint); +static unsigned int stop_whining = 0; +/* Print a complaint, and link the complaint block into a chain for + later handling. */ + +static void +vcomplain (struct complaints *complaints, struct complaint *complaint, + va_list args) +{ + char *msg; + enum complaint_series series; + struct cleanup *cleanups; + gdb_assert (complaints != NULL); complaint->counter++; if (complaint->next == NULL) { - complaint->next = complaint_root->next; - complaint_root->next = complaint; + complaint->next = complaints->root; + complaints->root = complaint; } if (complaint->counter > stop_whining) { @@ -76,93 +132,126 @@ } wrap_here (""); - switch (complaint_series + (info_verbose << 1)) - { + xvasprintf (&msg, complaint->message, args); + cleanups = make_cleanup (xfree, msg); - /* Isolated messages, must be self-explanatory. */ - case 0: - if (warning_hook) - (*warning_hook) (complaint->message, args); + if (info_verbose) + series = SUBSEQUENT_MESSAGE; + else + series = complaints->series; + + if (warning_hook) + (*warning_hook) (complaint->message, args); + else + { + if (series != SUBSEQUENT_MESSAGE) + begin_line (); + if (complaints->explanation == NULL) + fprintf_filtered (gdb_stderr, "%s.\n", msg); else - { - begin_line (); - fputs_filtered ("During symbol reading, ", gdb_stderr); - wrap_here (""); - vfprintf_filtered (gdb_stderr, complaint->message, args); - fputs_filtered (".\n", gdb_stderr); - } - break; + fprintf_filtered (gdb_stderr, + complaints->explanation[series], + msg); + } - /* First of a series, without `set verbose'. */ - case 1: - if (warning_hook) - (*warning_hook) (complaint->message, args); - else - { - begin_line (); - fputs_filtered ("During symbol reading...", gdb_stderr); - vfprintf_filtered (gdb_stderr, complaint->message, args); - fputs_filtered ("...", gdb_stderr); - wrap_here (""); - complaint_series++; - } + switch (series) + { + case ISOLATED_MESSAGE: break; + case FIRST_MESSAGE: + complaints->series = SUBSEQUENT_MESSAGE; + break; + case SUBSEQUENT_MESSAGE: + case SHORT_FIRST_MESSAGE: + complaints->series = SUBSEQUENT_MESSAGE; + break; + } - /* Subsequent messages of a series, or messages under `set verbose'. - (We'll already have produced a "Reading in symbols for XXX..." - message and will clean up at the end with a newline.) */ - default: - if (warning_hook) - (*warning_hook) (complaint->message, args); - else - { - vfprintf_filtered (gdb_stderr, complaint->message, args); - fputs_filtered ("...", gdb_stderr); - wrap_here (""); - } - } - /* If GDB dumps core, we'd like to see the complaints first. Presumably - GDB will not be sending so many complaints that this becomes a - performance hog. */ + wrap_here (""); + + /* If GDB dumps core, we'd like to see the complaints first. + Presumably GDB will not be sending so many complaints that this + becomes a performance hog. */ gdb_flush (gdb_stderr); + + do_cleanups (cleanups); +} + +void +complain (struct complaint *complaint, ...) +{ + va_list args; + va_start (args, complaint); + vcomplain (get_complaints (&symfile_complaints), complaint, args); + va_end (args); +} + +void +complaint (struct complaints **c, const char *fmt, ...) +{ + struct complaints *complaints = get_complaints (c); + struct complaint *complaint = complaints->root; + va_list args; + for (complaint = complaints->root; + complaint != NULL; + complaint = complaint->next) + { + if (complaint->message == fmt) + break; + } + if (complaint == NULL) + { + complaint = XMALLOC (struct complaint); + complaint->message = fmt; + complaint->counter = 0; + complaint->next = NULL; + } + va_start (args, fmt); + vcomplain (complaints, complaint, args); va_end (args); } -/* Clear out all complaint counters that have ever been incremented. - If sym_reading is 1, be less verbose about successive complaints, - since the messages are appearing all together during a command that - reads symbols (rather than scattered around as psymtabs get fleshed - out into symtabs at random times). If noisy is 1, we are in a - noisy symbol reading command, and our caller will print enough - context for the user to figure it out. */ +/* Clear out / initialize all complaint counters that have ever been + incremented. If LESS_VERBOSE is 1, be less verbose about + successive complaints, since the messages are appearing all + together during a command that is reporting a contiguous block of + complaints (rather than being interleaved with other messages). If + noisy is 1, we are in a noisy command, and our caller will print + enough context for the user to figure it out. */ void -clear_complaints (int sym_reading, int noisy) +clear_complaints (struct complaints **c, int less_verbose, int noisy) { + struct complaints *complaints = get_complaints (c); struct complaint *p; - for (p = complaint_root->next; p != complaint_root; p = p->next) + for (p = complaints->root; p != NULL; p = p->next) { p->counter = 0; } - if (!sym_reading && !noisy && complaint_series > 1 && !warning_hook) + if (complaints->series > 1 && !warning_hook) { /* Terminate previous series, since caller won't. */ puts_filtered ("\n"); } - complaint_series = sym_reading ? 1 + noisy : 0; + if (!less_verbose) + complaints->series = ISOLATED_MESSAGE; + else if (!noisy) + complaints->series = FIRST_MESSAGE; + else + complaints->series = SHORT_FIRST_MESSAGE; } void _initialize_complaints (void) { - add_show_from_set - (add_set_cmd ("complaints", class_support, var_zinteger, - (char *) &stop_whining, - "Set max number of complaints about incorrect symbols.", - &setlist), - &showlist); + add_setshow_cmd ("complaints", class_support, var_zinteger, + &stop_whining, + "Set max number of complaints about incorrect symbols.", + "Show max number of complaints about incorrect symbols.", + NULL, NULL, + &setlist, &showlist); } Index: complaints.h =================================================================== RCS file: /cvs/src/src/gdb/complaints.h,v retrieving revision 1.3 diff -u -r1.3 complaints.h --- complaints.h 6 Mar 2001 08:21:06 -0000 1.3 +++ complaints.h 15 Aug 2002 01:32:08 -0000 @@ -1,6 +1,7 @@ /* Definitions for complaint handling during symbol reading in GDB. - Copyright 1990, 1991, 1992, 1995, 1998, 2000 - Free Software Foundation, Inc. + + Copyright 1990, 1991, 1992, 1995, 1998, 2000, 2002 Free Software + Foundation, Inc. This file is part of GDB. @@ -23,6 +24,31 @@ #if !defined (COMPLAINTS_H) #define COMPLAINTS_H +/* Opaque object used to track the number of complaints of a + particular category. */ +struct complaints; + +/* Predefined categories. */ +extern struct complaints *symfile_complaints; + +/* Register a complaint. */ +extern void complaint (struct complaints **complaints, + const char *fmt, ...) ATTR_FORMAT (printf, 2, 3); + +/* Clear out / initialize all complaint counters that have ever been + incremented. If LESS_VERBOSE is 1, be less verbose about + successive complaints, since the messages are appearing all + together during a command that is reporting a contiguous block of + complaints (rather than being interleaved with other messages). If + noisy is 1, we are in a noisy command, and our caller will print + enough context for the user to figure it out. */ + +extern void clear_complaints (struct complaints **complaints, + int less_verbose, int noisy); + + +/* Legacy interfaces to keep the old code working (until it is all + converted to the above). */ /* Support for complaining about things in the symbol file that aren't catastrophic. @@ -32,22 +58,12 @@ if verbose, we report how many of each problem we had. */ struct complaint - { - char *message; - unsigned counter; - struct complaint *next; - }; - -/* Root of the chain of complaints that have at some point been issued. - This is used to reset the counters, and/or report the total counts. */ - -extern struct complaint complaint_root[1]; - -/* Functions that handle complaints. (in complaints.c) */ +{ + const char *message; + unsigned counter; + struct complaint *next; +}; extern void complain (struct complaint *, ...); - -extern void clear_complaints (int, int); - #endif /* !defined (COMPLAINTS_H) */ Index: symfile.c =================================================================== RCS file: /cvs/src/src/gdb/symfile.c,v retrieving revision 1.65 diff -u -r1.65 symfile.c --- symfile.c 1 Aug 2002 17:18:32 -0000 1.65 +++ symfile.c 15 Aug 2002 01:32:09 -0000 @@ -81,21 +81,6 @@ /* Global variables owned by this file */ int readnow_symbol_files; /* Read full symbols immediately */ -struct complaint oldsyms_complaint = -{ - "Replacing old symbols for `%s'", 0, 0 -}; - -struct complaint empty_symtab_complaint = -{ - "Empty symbol table found for `%s'", 0, 0 -}; - -struct complaint unknown_option_complaint = -{ - "Unknown option `%s' ignored", 0, 0 -}; - /* External variables and functions referenced. */ extern void report_transfer_performance (unsigned long, time_t, time_t); @@ -706,7 +691,7 @@ initial symbol reading for this file. */ (*objfile->sf->sym_init) (objfile); - clear_complaints (1, verbo); + clear_complaints (&symfile_complaints, 1, verbo); (*objfile->sf->sym_offsets) (objfile, addrs); @@ -818,7 +803,7 @@ } /* We're done reading the symbol file; finish off complaints. */ - clear_complaints (0, verbo); + clear_complaints (&symfile_complaints, 0, verbo); } /* Process a symbol file, as either the main file or as a dynamically @@ -1801,7 +1786,7 @@ } (*objfile->sf->sym_init) (objfile); - clear_complaints (1, 1); + clear_complaints (&symfile_complaints, 1, 1); /* The "mainline" parameter is a hideous hack; I think leaving it zero is OK since dbxread.c also does what it needs to do if objfile->global_psymbols.size is 0. */ @@ -1815,7 +1800,7 @@ objfile->flags |= OBJF_SYMS; /* We're done reading the symbol file; finish off complaints. */ - clear_complaints (0, 1); + clear_complaints (&symfile_complaints, 0, 1); /* Getting new symbols may change our opinion about what is frameless. */ @@ -2305,15 +2290,14 @@ || BLOCK_NSYMS (BLOCKVECTOR_BLOCK (bv, GLOBAL_BLOCK)) || BLOCK_NSYMS (BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK))) { - complain (&oldsyms_complaint, name); - + complaint ("Replacing old symbols for `%s'", name); clear_symtab_users_queued++; make_cleanup (clear_symtab_users_once, 0); blewit = 1; } else { - complain (&empty_symtab_complaint, name); + complaint ("Empty symbol table found for `%s'", name); } free_symtab (s); Index: testsuite/gdb.gdb/complaints.exp =================================================================== RCS file: testsuite/gdb.gdb/complaints.exp diff -N testsuite/gdb.gdb/complaints.exp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ testsuite/gdb.gdb/complaints.exp 15 Aug 2002 01:32:10 -0000 @@ -0,0 +1,282 @@ +# Copyright 2002 +# Free Software Foundation, Inc. + +# 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. + +# Please email any bugs, comments, and/or additions to this file to: +# bug-gdb@prep.ai.mit.edu + +# This file was written by Andrew Cagney (cagney at redhat dot com), +# derived from xfullpath.exp (written by Joel Brobecker), derived from +# selftest.exp (written by Rob Savoye). + +if $tracelevel then { + strace $tracelevel +} + +set prms_id 0 +set bug_id 0 + +# are we on a target board +if [is_remote target] { + return +} + +proc setup_test { executable } { + global gdb_prompt + global timeout + + # load yourself into the debugger + # This can take a relatively long time, particularly for testing where + # the executable is being accessed over a network, or where gdb does not + # support partial symbols for a particular target and has to load the + # entire symbol table. Set the timeout to 10 minutes, which should be + # adequate for most environments (it *has* timed out with 5 min on a + # SPARCstation SLC under moderate load, so this isn't unreasonable). + # After gdb is started, set the timeout to 30 seconds for the duration + # of this test, and then back to the original value. + + set oldtimeout $timeout + set timeout 600 + verbose "Timeout is now $timeout seconds" 2 + if {[gdb_load $executable] <0} then { + set timeout $oldtimeout + verbose "Timeout is now $timeout seconds" 2 + return -1 + } + set timeout $oldtimeout + verbose "Timeout is now $timeout seconds" 2 + + # Set a breakpoint at main + gdb_test "break captured_command_loop" \ + "Breakpoint.*at.* file.*, line.*" \ + "breakpoint in captured_command_loop" + + # run yourself + # It may take a very long time for the inferior gdb to start (lynx), + # so we bump it back up for the duration of this command. + set timeout 600 + + set description "run until breakpoint at captured_command_loop" + send_gdb "run -nw\n" + gdb_expect { + -re "Starting program.*Breakpoint \[0-9\]+,.*captured_command_loop .data.* at .*main.c:.*$gdb_prompt $" { + pass "$description" + } + -re "Starting program.*Breakpoint \[0-9\]+,.*captured_command_loop .data.*$gdb_prompt $" { + xfail "$description (line numbers scrambled?)" + } + -re "vfork: No more processes.*$gdb_prompt $" { + fail "$description (out of virtual memory)" + set timeout $oldtimeout + verbose "Timeout is now $timeout seconds" 2 + return -1 + } + -re ".*$gdb_prompt $" { + fail "$description" + set timeout $oldtimeout + verbose "Timeout is now $timeout seconds" 2 + return -1 + } + timeout { + fail "$description (timeout)" + } + } + + set timeout $oldtimeout + verbose "Timeout is now $timeout seconds" 2 + + return 0 +} + +proc test_isolated_complaints { } { + + global gdb_prompt + + # Unsupress complaints + gdb_test "set stop_whining = 2" + + # Prime the system + gdb_test "call complaint (&symfile_complaints, \"Register a complaint\")" \ + "During symbol reading, Register a complaint." + + # Check that the complaint was inserted and where + gdb_test "print symfile_complaints->root->message" \ + ".\[0-9\]+ =.*\"Register a complaint\"" + + # Re-issue the first message #1 + gdb_test "call complaint (&symfile_complaints, symfile_complaints->root->message)" \ + "During symbol reading, Register a complaint." + + # Check that there is only one thing in the list + gdb_test "print symfile_complaints->root->next == &complaint_sentinel" \ + ".\[0-9\]+ = 1" "list has one entry" + + # Add a second complaint, expect it + gdb_test "call complaint (&symfile_complaints, \"Testing! Testing! Testing!\")" \ + "During symbol reading, Testing. Testing. Testing.." + + return 0 +} + +proc test_serial_complaints { } { + + global gdb_prompt + + gdb_test_exact "call clear_complaints (&symfile_complaints, 1, 0)" "" "serial start" + + # Prime the system + send_gdb "call complaint (&symfile_complaints, \"serial line 1\")\n" + gdb_expect { + -re "During symbol reading...serial line 1...$gdb_prompt " { + pass "serial line 1" + } + "$gdb_prompt" { + fail "serial line 1" + } + timeout { + fail "serial line 1 (timeout)" + } + } + + # Add a second complaint, expect it + send_gdb "call complaint (&symfile_complaints, \"serial line 2\")\n" + gdb_expect { + -re "serial line 2...$gdb_prompt " { + pass "serial line 2" + } + "$gdb_prompt" { + fail "serial line 2" + } + timeout { + fail "serial line 2 (timeout)" + } + } + + send_gdb "call clear_complaints (&symfile_complaints, 1, 0)\n" + gdb_expect { + -re "\r\n\r\n$gdb_prompt " { + pass "serial end" + } + "$gdb_prompt" { + fail "serial end" + } + timeout { + fail "serial end (timeout)" + } + } + + return 0 +} + +# For short complaints, all are the same + +proc test_short_complaints { } { + + global gdb_prompt + + gdb_test_exact "call clear_complaints (&symfile_complaints, 1, 1)" "" "short start" + + # Prime the system + send_gdb "call complaint (&symfile_complaints, \"short line 1\")\n" + gdb_expect { + -re "short line 1...$gdb_prompt " { + pass "short line 1" + } + "$gdb_prompt" { + fail "short line 1" + } + timeout { + fail "short line 1 (timeout)" + } + } + + # Add a second complaint, expect it + send_gdb "call complaint (&symfile_complaints, \"short line 2\")\n" + gdb_expect { + -re "short line 2...$gdb_prompt " { + pass "short line 2" + } + "$gdb_prompt" { + fail "short line 2" + } + timeout { + fail "short line 2 (timeout)" + } + } + + send_gdb "call clear_complaints (&symfile_complaints, 1, 0)\n" + gdb_expect { + -re "\r\n\r\n$gdb_prompt " { + pass "short end" + } + "$gdb_prompt" { + fail "short end" + } + timeout { + fail "short end (timeout)" + } + } + + return 0 +} + +# Find a pathname to a file that we would execute if the shell was asked +# to run $arg using the current PATH. + +proc find_gdb { arg } { + + # If the arg directly specifies an existing executable file, then + # simply use it. + + if [file executable $arg] then { + return $arg + } + + set result [which $arg] + if [string match "/" [ string range $result 0 0 ]] then { + return $result + } + + # If everything fails, just return the unqualified pathname as default + # and hope for best. + + return $arg +} + +# Run the test with self. +# Copy the file executable file in case this OS doesn't like to edit its own +# text space. + +set GDB_FULLPATH [find_gdb $GDB] + +# Remove any old copy lying around. +remote_file host delete x$tool + +gdb_start + +set file [remote_download host $GDB_FULLPATH x$tool] + +set setup_result [setup_test $file ] +if {$setup_result <0} then { + return -1 +} + +test_isolated_complaints +test_serial_complaints +test_short_complaints + +gdb_exit; +catch "remote_file host delete $file";