From d17b6f81012b6844de08934193fe7cb7db8cbd5f Mon Sep 17 00:00:00 2001 From: Phil Muldoon Date: Thu, 21 Jul 2011 11:03:48 +0000 Subject: [PATCH] 2011-07-21 Phil Muldoon Tom Tromey * top.c (set_prompt): Rewrite to free previous prompt, free asynch_new_prompt and set both on new prompts. * event-top.c (display_gdb_prompt): Add prompt substitution logic. * python/python.c (before_prompt_hook): New function. 2011-07-21 Phil Muldoon * gdb.python/python.exp: Add prompt substitution tests. 2011-07-21 Phil Muldoon * observer.texi (GDB Observers): Add before_prompt observer. * gdb.texinfo (Basic Python): Add documentation for prompt substitution. --- gdb/ChangeLog | 9 ++++ gdb/doc/ChangeLog | 6 +++ gdb/doc/gdb.texinfo | 16 ++++++ gdb/doc/observer.texi | 5 ++ gdb/event-top.c | 62 ++++++++++++++++------ gdb/python/python.c | 80 ++++++++++++++++++++++++++++ gdb/testsuite/ChangeLog | 4 ++ gdb/testsuite/gdb.python/python.exp | 82 +++++++++++++++++++++++++++++ gdb/top.c | 17 +++--- gdb/top.h | 2 +- 10 files changed, 258 insertions(+), 25 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 5e4ab8c8e87..7b0b3b0bb66 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,12 @@ +2011-07-21 Phil Muldoon + Tom Tromey + + * top.c (set_prompt): Rewrite to free previous prompt, free + asynch_new_prompt and set both on new prompts. + * event-top.c (display_gdb_prompt): Add prompt substitution + logic. + * python/python.c (before_prompt_hook): New function. + 2011-07-20 Matt Rice * bfin-tdep.c (bfin_extract_return_value): Fix swapped diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 4a2d015d2b3..e3e02c8441f 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,9 @@ +2011-07-21 Phil Muldoon + + * observer.texi (GDB Observers): Add before_prompt observer. + * gdb.texinfo (Basic Python): Add documentation for prompt + substitution. + 2011-07-11 Phil Muldoon PR python/12438 diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 3a3a9fbf197..5b26bbbd7d6 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -21080,6 +21080,22 @@ provided, it is decoded the way that @value{GDBN}'s inbuilt @code{break} or @code{edit} commands do (@pxref{Specify Location}). @end defun +@defop Operation {@value{GDBN}} prompt_hook current_prompt +If @var{prompt_hook} is callable, @value{GDBN} will call the method +assigned to this operation before a prompt is displayed by +@value{GDBN}. + +The parameter @code{current_prompt} contains the current @value{GDBN} +prompt. This method must return a Python string, or @code{None}. If +a string is returned, the @value{GDBN} prompt will be set to that +string. If @code{None} is returned, @value{GDBN} will continue to use +the current prompt. + +Some prompts cannot be substituted in @value{GDBN}. Secondary prompts +such as those used by readline for command input, and annotation +related prompts are prohibited from being changed. +@end defop + @node Exception Handling @subsubsection Exception Handling @cindex python exceptions diff --git a/gdb/doc/observer.texi b/gdb/doc/observer.texi index d8c39247894..689b3039f4c 100644 --- a/gdb/doc/observer.texi +++ b/gdb/doc/observer.texi @@ -222,6 +222,11 @@ Bytes from @var{data} to @var{data} + @var{len} have been written to the current inferior at @var{addr}. @end deftypefun +@deftypefun void before_prompt (const char *@var{current_prompt}) +Called before a top-level prompt is displayed. @var{current_prompt} is +the current top-level prompt. +@end deftypefun + @deftypefun void test_notification (int @var{somearg}) This observer is used for internal testing. Do not use. See testsuite/gdb.gdb/observer.exp. diff --git a/gdb/event-top.c b/gdb/event-top.c index 72cbfdcfc6e..22f94409acf 100644 --- a/gdb/event-top.c +++ b/gdb/event-top.c @@ -33,6 +33,7 @@ #include "cli/cli-script.h" /* for reset_command_nest_depth */ #include "main.h" #include "gdbthread.h" +#include "observer.h" #include "continuations.h" #include "gdbcmd.h" /* for dont_repeat() */ @@ -258,7 +259,7 @@ void display_gdb_prompt (char *new_prompt) { int prompt_length = 0; - char *gdb_prompt = get_prompt (); + char *actual_gdb_prompt = NULL; /* Reset the nesting depth used when trace-commands is set. */ reset_command_nest_depth (); @@ -268,6 +269,25 @@ display_gdb_prompt (char *new_prompt) if (!current_interp_display_prompt_p ()) return; + /* Get the prompt before the observers are called as observer hook + functions may change the prompt. Do not call observers on an + explicit prompt change as passed to this function, as this forms + a temporary prompt, IE, displayed but not set. */ + if (! new_prompt) + { + char *post_gdb_prompt = NULL; + char *pre_gdb_prompt = xstrdup (get_prompt ()); + + observer_notify_before_prompt (pre_gdb_prompt); + post_gdb_prompt = get_prompt (); + + /* If the observer changed the prompt, use that prompt. */ + if (strcmp (pre_gdb_prompt, post_gdb_prompt) != 0) + actual_gdb_prompt = post_gdb_prompt; + + xfree (pre_gdb_prompt); + } + if (sync_execution && is_running (inferior_ptid)) { /* This is to trick readline into not trying to display the @@ -289,27 +309,35 @@ display_gdb_prompt (char *new_prompt) return; } - if (!new_prompt) + /* If the observer changed the prompt, ACTUAL_GDB_PROMPT will not be + NULL. Otherwise, either copy the existing prompt, or set it to + NEW_PROMPT. */ + if (! actual_gdb_prompt) { - /* Just use the top of the prompt stack. */ - prompt_length = strlen (PREFIX (0)) + - strlen (SUFFIX (0)) + - strlen (gdb_prompt) + 1; - - new_prompt = (char *) alloca (prompt_length); - - /* Prefix needs to have new line at end. */ - strcpy (new_prompt, PREFIX (0)); - strcat (new_prompt, gdb_prompt); - /* Suffix needs to have a new line at end and \032 \032 at - beginning. */ - strcat (new_prompt, SUFFIX (0)); + if (! new_prompt) + { + /* Just use the top of the prompt stack. */ + prompt_length = strlen (PREFIX (0)) + + strlen (SUFFIX (0)) + + strlen (get_prompt()) + 1; + + actual_gdb_prompt = (char *) alloca (prompt_length); + + /* Prefix needs to have new line at end. */ + strcpy (actual_gdb_prompt, PREFIX (0)); + strcat (actual_gdb_prompt, get_prompt()); + /* Suffix needs to have a new line at end and \032 \032 at + beginning. */ + strcat (actual_gdb_prompt, SUFFIX (0)); + } + else + actual_gdb_prompt = new_prompt;; } if (async_command_editing_p) { rl_callback_handler_remove (); - rl_callback_handler_install (new_prompt, input_handler); + rl_callback_handler_install (actual_gdb_prompt, input_handler); } /* new_prompt at this point can be the top of the stack or the one passed in. It can't be NULL. */ @@ -318,7 +346,7 @@ display_gdb_prompt (char *new_prompt) /* Don't use a _filtered function here. It causes the assumed character position to be off, since the newline we read from the user is not accounted for. */ - fputs_unfiltered (new_prompt, gdb_stdout); + fputs_unfiltered (actual_gdb_prompt, gdb_stdout); gdb_flush (gdb_stdout); } } diff --git a/gdb/python/python.c b/gdb/python/python.c index f15936dae3c..e3a8d195e45 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -51,6 +51,7 @@ static int gdbpy_should_print_stack = 0; #include "version.h" #include "target.h" #include "gdbthread.h" +#include "observer.h" static PyMethodDef GdbMethods[]; @@ -682,6 +683,81 @@ gdbpy_initialize_events (void) } } + + +static void +before_prompt_hook (const char *current_gdb_prompt) +{ + struct cleanup *cleanup; + char *prompt = NULL; + + cleanup = ensure_python_env (get_current_arch (), current_language); + + if (PyObject_HasAttrString (gdb_module, "prompt_hook")) + { + PyObject *hook; + + hook = PyObject_GetAttrString (gdb_module, "prompt_hook"); + if (hook == NULL) + goto fail; + + if (PyCallable_Check (hook)) + { + PyObject *result; + PyObject *current_prompt; + + current_prompt = PyString_FromString (current_gdb_prompt); + if (current_prompt == NULL) + goto fail; + + result = PyObject_CallFunctionObjArgs (hook, current_prompt, NULL); + + Py_DECREF (current_prompt); + + if (result == NULL) + goto fail; + + make_cleanup_py_decref (result); + + /* Return type should be None, or a String. If it is None, + fall through, we will not set a prompt. If it is a + string, set PROMPT. Anything else, set an exception. */ + if (result != Py_None && ! PyString_Check (result)) + { + PyErr_Format (PyExc_RuntimeError, + _("Return from prompt_hook must " \ + "be either a Python string, or None")); + goto fail; + } + + if (result != Py_None) + { + prompt = python_string_to_host_string (result); + + if (prompt == NULL) + goto fail; + else + make_cleanup (xfree, prompt); + } + } + } + + /* If a prompt has been set, PROMPT will not be NULL. If it is + NULL, do not set the prompt. */ + if (prompt != NULL) + set_prompt (prompt); + + do_cleanups (cleanup); + return; + + fail: + gdbpy_print_stack (); + do_cleanups (cleanup); + return; +} + + + /* Printing. */ /* A python function to write a single string using gdb's filtered @@ -1134,6 +1210,8 @@ Enables or disables printing of Python stack traces."), gdbpy_initialize_exited_event (); gdbpy_initialize_thread_event (); + observer_attach_before_prompt (before_prompt_hook); + PyRun_SimpleString ("import gdb"); PyRun_SimpleString ("gdb.pretty_printers = []"); @@ -1236,6 +1314,8 @@ def GdbSetPythonDirectory (dir):\n\ \n\ # Install the default gdb.PYTHONDIR.\n\ GdbSetPythonDirectory (gdb.PYTHONDIR)\n\ +# Default prompt hook does nothing.\n\ +prompt_hook = None\n\ "); do_cleanups (cleanup); diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index e3b3adb8ec9..ac815ca20e6 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2011-07-21 Phil Muldoon + + * gdb.python/python.exp: Add prompt substitution tests. + 2011-07-19 Jan Kratochvil Fix crash if referenced CU is aged out. diff --git a/gdb/testsuite/gdb.python/python.exp b/gdb/testsuite/gdb.python/python.exp index 8906bbc97a3..832afc0eded 100644 --- a/gdb/testsuite/gdb.python/python.exp +++ b/gdb/testsuite/gdb.python/python.exp @@ -193,3 +193,85 @@ gdb_py_test_silent_cmd "set python print-stack on" \ "Test print-backtrace set setting" 1 gdb_test "show python print-stack" \ "Whether Python stack will be printed on error is on.*" \ + +# Test prompt substituion + +gdb_py_test_multiple "prompt substitution" \ + "python" "" \ + "someCounter = 0" "" \ + "def prompt(current):" "" \ + " global someCounter" "" \ + " if (current == \"testfake \"):" "" \ + " return None" "" \ + " someCounter = someCounter + 1" "" \ + " return \"py prompt \" + str (someCounter) + \" \"" "" \ + "end" "" + +gdb_py_test_multiple "prompt substitution readline" \ + "python" "" \ + "pCounter = 0" "" \ + "def program_prompt(current):" "" \ + " global pCounter" "" \ + " if (current == \">\"):" "" \ + " pCounter = pCounter + 1" "" \ + " return \"python line \" + str (pCounter) + \": \"" "" \ + " return None" "" \ + "end" "" + +set newprompt "py prompt 1" +set newprompt2 "py prompt 2" +set testfake "testfake" + +gdb_test_multiple "python gdb.prompt_hook = prompt" "set the hook" { + -re "\[\r\n\]$newprompt $" { + pass "set hook" + } +} + +gdb_test_multiple "set prompt testfake " "set testfake prompt in GDB" { + -re "\[\r\n\]$testfake $" { + pass "set prompt testfake" + } +} + +gdb_test_multiple "show prompt" "show testfake prompt" { + -re "Gdb's prompt is \"$testfake \"..* $" { + pass "show prompt shows guarded prompt" + } +} + +gdb_test_multiple "set prompt blah " "set blah in GDB" { + -re "\[\r\n\]$newprompt2 $" { + pass "set prompt blah overriden" + } +} + +gdb_test_multiple "python gdb.prompt_hook = None" "Delete hook" { + -re "\[\r\n\]$newprompt2 $" { + pass "Delete old hook" + } +} + +gdb_test_multiple "set prompt $gdb_prompt " "set default prompt" { + -re "\[\r\n\]$gdb_prompt $" { + pass "set default prompt" + } +} + +gdb_test_multiple "python gdb.prompt_hook = program_prompt" "set the hook" { + -re "\[\r\n\]$gdb_prompt $" { + pass "set programming hook" + } +} + +gdb_test_multiple "python" "test we ignore substituion for seconday prompts" { + -re "\r\n>$" { + pass "readline secondary are not substituted" + } +} + +gdb_test_multiple "end" "end programming" { + -re "\[\r\n\]$gdb_prompt $" { + pass "end programming" + } +} diff --git a/gdb/top.c b/gdb/top.c index ecb91f2b0ef..3ffd0002575 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -1133,14 +1133,17 @@ get_prompt (void) } void -set_prompt (char *s) +set_prompt (const char *s) { -/* ??rehrauer: I don't know why this fails, since it looks as though - assignments to prompt are wrapped in calls to xstrdup... - if (prompt != NULL) - xfree (prompt); - */ - PROMPT (0) = xstrdup (s); + char *p = xstrdup (s); + + xfree (PROMPT (0)); + PROMPT (0) = p; + + /* Also, free and set new_async_prompt so prompt changes sync up + with set/show prompt. */ + xfree (new_async_prompt); + new_async_prompt = xstrdup (PROMPT (0)); } diff --git a/gdb/top.h b/gdb/top.h index 4a6e8fba870..24ec2f20c51 100644 --- a/gdb/top.h +++ b/gdb/top.h @@ -55,7 +55,7 @@ extern char *get_prompt (void); /* This function copies the specified string into the string that is used by gdb for its command prompt. */ -extern void set_prompt (char *); +extern void set_prompt (const char *); /* From random places. */ extern int readnow_symbol_files; -- 2.30.2