[PATCH 2/2] Implement completion limiting
Gary Benson
gbenson@redhat.com
Wed Feb 12 11:58:00 GMT 2014
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 <gbenson@redhat.com>
* exceptions.h (enum errors) <TOO_MANY_COMPLETIONS_ERROR>:
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) <n_global_syms>: 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 <gbenson@redhat.com>
* gdb.texinfo (Command Completion): Document new
"set/show max-completions" option.
gdb/testsuite/
2014-02-12 Gary Benson <gbenson@redhat.com>
* 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
More information about the Gdb-patches
mailing list