From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 29611 invoked by alias); 12 Feb 2014 11:58:05 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 29562 invoked by uid 89); 12 Feb 2014 11:58:05 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.4 required=5.0 tests=AWL,BAYES_00,KAM_STOCKGEN,RP_MATCHES_RCVD,SPF_HELO_PASS,SPF_PASS autolearn=no version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 12 Feb 2014 11:58:03 +0000 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s1CBw2AK016032 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Wed, 12 Feb 2014 06:58:02 -0500 Received: from blade.nx (ovpn-116-103.ams2.redhat.com [10.36.116.103]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s1CBw0BW008118 for ; Wed, 12 Feb 2014 06:58:01 -0500 Received: by blade.nx (Postfix, from userid 1000) id 2FE502643A1; Wed, 12 Feb 2014 11:57:59 +0000 (GMT) Date: Wed, 12 Feb 2014 11:58:00 -0000 From: Gary Benson To: gdb-patches@sourceware.org Subject: [PATCH 2/2] Implement completion limiting Message-ID: <20140212115759.GC2866@blade.nx> References: <20140212115548.GA2866@blade.nx> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20140212115548.GA2866@blade.nx> X-IsSubscribed: yes X-SW-Source: 2014-02/txt/msg00400.txt.bz2 This patch adds a new exception, TOO_MANY_COMPLETIONS_ERROR, to be thrown whenever the completer has generated too many possibilities to be useful. A new user-settable variable, "max_completions", is added to control this behaviour. gdb/ 2014-02-12 Gary Benson * exceptions.h (enum errors) : New error. * completer.c: Include TUI headers when appropriate. (max_completions): New variable. (complete_line): Added completion limiting logic and associated cleanup. (line_completion_function): Handle TOO_MANY_COMPLETIONS_ERROR. (limit_completions): New function. (_initialize_completer): Likewise. * completer.h (limit_completions): New declaration. * symtab.c: Include psymtab.h, psympriv.h and completer.h. (struct add_name_data) : New field. (halt_large_expansions): New function. (default_make_symbol_completion_list_break_on): Added completion limiting logic. gdb/doc/ 2014-02-12 Gary Benson * gdb.texinfo (Command Completion): Document new "set/show max-completions" option. gdb/testsuite/ 2014-02-12 Gary Benson * gdb.base/completion.exp: Test completion limiting. diff --git a/gdb/exceptions.h b/gdb/exceptions.h index b8dadc7..1b9c1ef 100644 --- a/gdb/exceptions.h +++ b/gdb/exceptions.h @@ -100,6 +100,9 @@ enum errors { /* Requested feature, method, mechanism, etc. is not supported. */ NOT_SUPPORTED_ERROR, + /* Too many possibilities were encountered during line completion. */ + TOO_MANY_COMPLETIONS_ERROR, + /* Add more errors here. */ NR_ERRORS }; diff --git a/gdb/completer.c b/gdb/completer.c index 94f70a9..31e300f 100644 --- a/gdb/completer.c +++ b/gdb/completer.c @@ -41,6 +41,11 @@ #include "completer.h" +#ifdef TUI +#include "tui/tui.h" +#include "tui/tui-io.h" +#endif + /* Prototypes for local functions. */ static char *line_completion_function (const char *text, int matches, @@ -759,9 +764,17 @@ complete_line_internal (const char *text, return list; } -/* Generate completions all at once. Returns a vector of strings. - Each element is allocated with xmalloc. It can also return NULL if - there are no completions. + +/* Maximum number of possibilities to consider before the completer + bails by throwing TOO_MANY_COMPLETIONS_ERROR. If set to -1 then + no limiting will be performed. */ +static int max_completions = 1000; + +/* Generate completions all at once. Returns a vector of strings + allocated with xmalloc. Returns NULL if there are no completions + or if max_completions is 0. Throws TOO_MANY_COMPLETIONS_ERROR if + max_completions is greater than zero and the number of completions + is greater than max_completions. TEXT is the caller's idea of the "word" we are looking at. @@ -774,8 +787,26 @@ complete_line_internal (const char *text, VEC (char_ptr) * complete_line (const char *text, char *line_buffer, int point) { - return complete_line_internal (text, line_buffer, - point, handle_completions); + VEC (char_ptr) *list = NULL; + + if (max_completions != 0) + { + struct cleanup *back_to; + + list = complete_line_internal (text, line_buffer, + point, handle_completions); + back_to = make_cleanup_free_char_ptr_vec (list); + + /* Possibly throw TOO_MANY_COMPLETIONS_ERROR. Individual + completers may do this too, to avoid unnecessary work, + but this is the ultimate check that stops users seeing + more completions than they wanted. */ + limit_completions (VEC_length (char_ptr, list)); + + discard_cleanups (back_to); + } + + return list; } /* Complete on command names. Used by "help". */ @@ -862,6 +893,8 @@ line_completion_function (const char *text, int matches, if (matches == 0) { + volatile struct gdb_exception ex; + /* The caller is beginning to accumulate a new set of completions, so we need to find all of them now, and cache them for returning one at a time on future calls. */ @@ -875,7 +908,35 @@ line_completion_function (const char *text, int matches, VEC_free (char_ptr, list); } index = 0; - list = complete_line (text, line_buffer, point); + + TRY_CATCH (ex, RETURN_MASK_ALL) + list = complete_line (text, line_buffer, point); + + if (ex.reason < 0) + { + if (ex.error != TOO_MANY_COMPLETIONS_ERROR) + throw_exception (ex); + + if (rl_completion_type != TAB) + { +#if defined(TUI) + if (tui_active) + { + tui_puts ("\n"); + tui_puts (ex.message); + tui_puts ("\n"); + } + else +#endif + { + rl_crlf (); + fputs (ex.message, rl_outstream); + rl_crlf (); + } + + rl_on_new_line (); + } + } } /* If we found a list of potential completions during initialization @@ -959,3 +1020,31 @@ skip_quoted (const char *str) { return skip_quoted_chars (str, NULL, NULL); } + +/* Throw TOO_MANY_COMPLETIONS_ERROR if max_completions is greater than + zero and NUM_COMPLETIONS is greater than max_completions. Negative + values of max_completions disable limiting. */ + +void +limit_completions (int num_completions) +{ + if (max_completions >= 0 && num_completions > max_completions) + throw_error (TOO_MANY_COMPLETIONS_ERROR, + _("Too many possibilities.")); +} + +extern initialize_file_ftype _initialize_completer; /* -Wmissing-prototypes */ + +void +_initialize_completer (void) +{ + add_setshow_zuinteger_unlimited_cmd ("max-completions", no_class, + &max_completions, _("\ +Set maximum number of line completion possibilities."), _("\ +Show maximum number of line completion possibilities."), _("\ +Use this to limit the number of possibilities considered\n\ +during line completion. Specifying \"unlimited\" or -1\n\ +disables limiting. Note that setting either no limit or\n\ +a very large limit can cause pauses during completion."), + NULL, NULL, &setlist, &showlist); +} diff --git a/gdb/completer.h b/gdb/completer.h index 5b90773..58c726d 100644 --- a/gdb/completer.h +++ b/gdb/completer.h @@ -48,6 +48,8 @@ extern char *get_gdb_completer_quote_characters (void); extern char *gdb_completion_word_break_characters (void); +extern void limit_completions (int); + /* Exported to linespec.c */ extern const char *skip_quoted_chars (const char *, const char *, diff --git a/gdb/symtab.c b/gdb/symtab.c index 0b5d34a..9faf496 100644 --- a/gdb/symtab.c +++ b/gdb/symtab.c @@ -62,6 +62,9 @@ #include "macroscope.h" #include "parser-defs.h" +#include "psymtab.h" +#include "psympriv.h" +#include "completer.h" /* Prototypes for local functions */ @@ -4274,6 +4277,7 @@ struct add_name_data int sym_text_len; const char *text; const char *word; + int n_global_syms; }; /* A callback used with macro_for_each and macro_for_each_in_scope. @@ -4301,6 +4305,23 @@ symbol_completion_matcher (const char *name, void *user_data) return compare_symbol_name (name, datum->sym_text, datum->sym_text_len); } +/* A callback for expand_partial_symbol_names, used to abort line + completion before large numbers of symbols have been expanded. + Without this check GDB can appear to lock up during some line + completions. This is an inexact but worst-case check, in that + there will be more than the threshold number of completions + available by the time limit_completions bails. */ + +static void +halt_large_expansions (struct objfile *objfile, + struct partial_symtab *pst, void *user_data) +{ + struct add_name_data *datum = (struct add_name_data *) user_data; + + datum->n_global_syms += pst->n_global_syms; + limit_completions (datum->n_global_syms); +} + VEC (char_ptr) * default_make_symbol_completion_list_break_on (const char *text, const char *word, @@ -4401,12 +4422,13 @@ default_make_symbol_completion_list_break_on (const char *text, datum.sym_text_len = sym_text_len; datum.text = text; datum.word = word; + datum.n_global_syms = 0; /* Look through the partial symtabs for all symbols which begin by matching SYM_TEXT. Expand all CUs that you find to the list. The real names will get added by COMPLETION_LIST_ADD_SYMBOL below. */ - expand_symtabs_matching (NULL, symbol_completion_matcher, NULL, - ALL_DOMAIN, &datum); + expand_symtabs_matching (NULL, symbol_completion_matcher, + halt_large_expansions, ALL_DOMAIN, &datum); /* At this point scan through the misc symbol vectors and add each symbol you find to the list. Eventually we want to ignore diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 035573e..8d7a6fe 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -1597,6 +1597,30 @@ means @kbd{@key{META} ?}. You can type this either by holding down a key designated as the @key{META} shift on your keyboard (if there is one) while typing @kbd{?}, or as @key{ESC} followed by @kbd{?}. +If the number of possible completions is large, @value{GDBN} will +print a message rather than displaying the list: + +@smallexample +(@value{GDBP}) b @key{TAB}@key{TAB} +Too many possibilities. +@end smallexample + +@noindent +This behavior can be controlled with the following commands: + +@table @code +@kindex set max-completions +@item set max-completions @var{limit} +@itemx set max-completions unlimited +Set the maximum number of possibilities to be considered during +completion. The default value is 1000. Note that setting either +no limit or a very large limit can cause pauses during completion. +@kindex show max-completions +@item show max-completions +Show the maximum number of possibilities to be considered during +completion. +@end table + @cindex quotes in commands @cindex completion of quoted strings Sometimes the string you need, while logically a ``word'', may contain diff --git a/gdb/testsuite/gdb.base/completion.exp b/gdb/testsuite/gdb.base/completion.exp index d51a847..721c17a 100644 --- a/gdb/testsuite/gdb.base/completion.exp +++ b/gdb/testsuite/gdb.base/completion.exp @@ -69,6 +69,8 @@ if ![runto_main] then { set oldtimeout1 $timeout set timeout 30 +gdb_test_no_output "set max-completions unlimited" + set test "complete 'hfgfh'" send_gdb "hfgfh\t" gdb_test_multiple "" "$test" { @@ -729,6 +731,38 @@ gdb_test "complete set listsize unl" "set listsize unlimited" gdb_test "complete set trace-buffer-size " "set trace-buffer-size unlimited" gdb_test "complete set trace-buffer-size unl" "set trace-buffer-size unlimited" +# +# Completion limiting. +# + +gdb_test_no_output "set max-completions 5" + +set test "completion limiting using tab character" +send_gdb "p\t" +gdb_test_multiple "" "$test" { + -re "^p\\\x07$" { + send_gdb "\t" + gdb_test_multiple "" "$test" { + -re "Too many possibilities.\r\n\\\x07$gdb_prompt p$" { + send_gdb "\n" + gdb_test_multiple "" "$test" { + -re "$gdb_prompt $" { + pass "$test" + } + } + } + } + } +} + +set test "completion limiting using complete command" +send_gdb "complete p\n" +gdb_test_multiple "" "$test" { + -re "Too many possibilities.\r\n$gdb_prompt $" { + pass "$test" + } +} + # Restore globals modified in this test... set timeout $oldtimeout1