This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 2/2] Implement completion limiting


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
 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]