[v2] Add a way to preserve overridden GDB commands for later invocation

Message ID 20191101085449.28493-1-mbarisione@undo.io
State New, archived
Headers

Commit Message

Marco Barisione Nov. 1, 2019, 8:54 a.m. UTC
  When a new Python command overrides an existing command, the
functionality of the original command used to be lost.  As not all
features are exposed through the Python API, this makes it impossile to
build around existing commands.  This patch adds support for preserving
commands when overridden.

This feature is exposed through the Python API with a new
gdb.Command.invoke_overridden () method which can be called from the
implementation of the invoke () method.
To avoid unexpected behavioural changes, new commands implemented in
Python are, by default, freed when overridden.  A command which wants
the new behaviour can pass the new preserve_when_overridden argument to
gdb.Command.__init__ ().

All default commands (that is, the ones defined by GDB itself) are
preserved when overridden.

All new commands added thorugh GDB scripting (i.e. using the define
command) are not preserved when overridden.  This could be changed in a
later patch but it doesn't seem important (nor useful) at the moment.

This change is achieved by adding a preserve_when_overridden field to
struct cmd_list_element.  If true, when another command overrides it,
the destroyer function is not called and the previous command is saved
in the preserved_overridden_cmd field.  This way, the overridden command
is still accessible if needed.

2019-11-01  Marco Barisione  <mbarisione@undo.io>

	Add support for preserving the functionalities from overridden
	commands.
	* cli/cli-decode.c (delete_cmd): Rename to remove_cmd.
	(remove_cmd): Allow commands to be preserved after overriding
	them.
	(do_add_cmd): Support the preservation of overridden commands.
	(add_alias_cmd): Use delete_cmd instead of remove_cmd and update
	the assertions.
	* cli/cli-decode.h (struct cmd_list_element): Add
	preserve_when_overridden and preserved_overridden_cmd.
	* cli/cli-script.c (do_define_command): Make default commands
	be preserved by default.
	* python/py-cmd.c (cmdpy_invoke_overridden): Add an
	invoke_overridden command method to gdb.Command.
	(cmdpy_init): Add a preserve_when_overridden argument to
	gdb.Command.__init__.
	* NEWS: Document the addition of gdb.Command.invoke_overridden
	and the new preserve_when_overridden argument for
	gdb.Command.__init__.

gdb/doc/ChangeLog:

2019-11-01  Marco Barisione  <mbarisione@undo.io>

	* python.texi (Commands In Python): Document
	gdb.Command.invoke_overridden and the new
	preserve_when_overridden argument for gdb.Command.__init__.

gdb/testsuite/ChangeLog:

2019-11-01  Marco Barisione  <mbarisione@undo.io>

	* gdb.python/py-chain-invoke.exp: New file.
	* gdb.python/py-chain-invoke.py: New test.
---
 gdb/NEWS                                     |   8 +
 gdb/cli/cli-decode.c                         |  84 +++--
 gdb/cli/cli-decode.h                         |  10 +
 gdb/cli/cli-script.c                         |   3 +
 gdb/doc/python.texi                          |  33 +-
 gdb/python/py-cmd.c                          |  54 +++-
 gdb/testsuite/gdb.python/py-chain-invoke.exp | 315 +++++++++++++++++++
 gdb/testsuite/gdb.python/py-chain-invoke.py  |  72 +++++
 8 files changed, 555 insertions(+), 24 deletions(-)
 create mode 100644 gdb/testsuite/gdb.python/py-chain-invoke.exp
 create mode 100644 gdb/testsuite/gdb.python/py-chain-invoke.py
  

Comments

Eli Zaretskii Nov. 1, 2019, 9:14 a.m. UTC | #1
> From: Marco Barisione <mbarisione@undo.io>
> Date: Fri,  1 Nov 2019 08:54:49 +0000
> 
> 2019-11-01  Marco Barisione  <mbarisione@undo.io>
> 
> 	Add support for preserving the functionalities from overridden
> 	commands.
> 	* cli/cli-decode.c (delete_cmd): Rename to remove_cmd.
> 	(remove_cmd): Allow commands to be preserved after overriding
> 	them.
> 	(do_add_cmd): Support the preservation of overridden commands.
> 	(add_alias_cmd): Use delete_cmd instead of remove_cmd and update
> 	the assertions.
> 	* cli/cli-decode.h (struct cmd_list_element): Add
> 	preserve_when_overridden and preserved_overridden_cmd.
> 	* cli/cli-script.c (do_define_command): Make default commands
> 	be preserved by default.
> 	* python/py-cmd.c (cmdpy_invoke_overridden): Add an
> 	invoke_overridden command method to gdb.Command.
> 	(cmdpy_init): Add a preserve_when_overridden argument to
> 	gdb.Command.__init__.
> 	* NEWS: Document the addition of gdb.Command.invoke_overridden
> 	and the new preserve_when_overridden argument for
> 	gdb.Command.__init__.
> 
> gdb/doc/ChangeLog:
> 
> 2019-11-01  Marco Barisione  <mbarisione@undo.io>
> 
> 	* python.texi (Commands In Python): Document
> 	gdb.Command.invoke_overridden and the new
> 	preserve_when_overridden argument for gdb.Command.__init__.
> 
> gdb/testsuite/ChangeLog:
> 
> 2019-11-01  Marco Barisione  <mbarisione@undo.io>
> 
> 	* gdb.python/py-chain-invoke.exp: New file.
> 	* gdb.python/py-chain-invoke.py: New test.

Thanks, the documentation parts are OK.
  
Tom Tromey Nov. 1, 2019, 7:18 p.m. UTC | #2
>>>>> "Marco" == Marco Barisione <mbarisione@undo.io> writes:

Thanks for the patch.  I think it's a good idea -- in fact, I've wanted
it myself before :-)

Do you have a copyright assignment on file?

Marco> To avoid unexpected behavioural changes, new commands implemented in
Marco> Python are, by default, freed when overridden.  A command which wants
Marco> the new behaviour can pass the new preserve_when_overridden argument to
Marco> gdb.Command.__init__ ().

Could you say what unexpected behavioural changes you would anticipate?
I tend to think it would be clearer if all commands were treated
identically.

Is there some implementation difficulty doing it?  Or was it just that
you didn't think it was useful?

In this model, if a Python command overrides a built-in command, and
then is itself overridden, can the new command still access the
underlying built-in command?

What happens in the weird case that you have a command alias X, then
override X, and then override the thing that the original X was aliased
to?  Will calling the overridden X do the right thing?  Really I'm
wondering if that crashes -- maybe a counter-argument to my wish for
generality is that aliases should not be overridden.

I skimmed the patch and it seemed ok but I figured we'd want to address
these things first.

thanks,
Tom
  
Marco Barisione Nov. 1, 2019, 9:01 p.m. UTC | #3
On 1 Nov 2019, at 19:18, Tom Tromey <tom@tromey.com> wrote:
>>>>>> "Marco" == Marco Barisione <mbarisione@undo.io> writes:
> Could you say what unexpected behavioural changes you would anticipate?
> I tend to think it would be clearer if all commands were treated
> identically.
> 
> Is there some implementation difficulty doing it?  Or was it just that
> you didn't think it was useful?

I was mainly worried about changing the behaviour in ways which could be
unexpected.  For instance, an instance of gdb.Command may end up living longer
than expected.
Another thing is that a command which was written before this patch may not be
using the original implementation just because it was not possible before, so
block a further command from accessing it as well.  See the example later in
the email.

If you don't think these are problems I'm happy to simplify the code.

> In this model, if a Python command overrides a built-in command, and
> then is itself overridden, can the new command still access the
> underlying built-in command?

Let's say you override delete. Let's call the first class overriding it
DeleteCommand1 and the second DeleteCommand2.

If DeleteCommand1 didn't use preserve_when_overridden then
DeleteCommand2.invoke_overridden will just call the original delete.

If DeleteCommand1 was preserved, then DeleteCommand2.invoke_overridden
will call DeleteCommand1.invoke.  At this point DeleteCommand1 may just do
whatever it needs on its own, or call its own invoke_overridden method
which will call the original delete command.
DeleteCommand2 has not way to invoke the original delete command directly.
This is partly because I'm not sure how I would expose this in a nice way,
but mainly because I don't want commands to accidentally skip some
previous implementation by accident.  For instance, if we had an
invoke_original method, then DeleteCommand2 could call that and skip
DeleteCommand1.

> What happens in the weird case that you have a command alias X, then
> override X, and then override the thing that the original X was aliased
> to?  Will calling the overridden X do the right thing?  Really I'm
> wondering if that crashes -- maybe a counter-argument to my wish for
> generality is that aliases should not be overridden.

Oh right, aliases are treated a bit differently and I didn't think of
testing that.  I should add a test and see.
  
Andrew Burgess Nov. 5, 2019, 10:17 a.m. UTC | #4
* Marco Barisione <mbarisione@undo.io> [2019-11-01 21:01:18 +0000]:

> On 1 Nov 2019, at 19:18, Tom Tromey <tom@tromey.com> wrote:
> >>>>>> "Marco" == Marco Barisione <mbarisione@undo.io> writes:
> > Could you say what unexpected behavioural changes you would anticipate?
> > I tend to think it would be clearer if all commands were treated
> > identically.
> > 
> > Is there some implementation difficulty doing it?  Or was it just that
> > you didn't think it was useful?
> 
> I was mainly worried about changing the behaviour in ways which could be
> unexpected.  For instance, an instance of gdb.Command may end up living longer
> than expected.
> Another thing is that a command which was written before this patch may not be
> using the original implementation just because it was not possible before, so
> block a further command from accessing it as well.  See the example later in
> the email.
> 
> If you don't think these are problems I'm happy to simplify the code.
> 
> > In this model, if a Python command overrides a built-in command, and
> > then is itself overridden, can the new command still access the
> > underlying built-in command?
> 
> Let's say you override delete. Let's call the first class overriding it
> DeleteCommand1 and the second DeleteCommand2.
> 
> If DeleteCommand1 didn't use preserve_when_overridden then
> DeleteCommand2.invoke_overridden will just call the original delete.
> 
> If DeleteCommand1 was preserved, then DeleteCommand2.invoke_overridden
> will call DeleteCommand1.invoke.  At this point DeleteCommand1 may just do
> whatever it needs on its own, or call its own invoke_overridden method
> which will call the original delete command.
> DeleteCommand2 has not way to invoke the original delete command directly.
> This is partly because I'm not sure how I would expose this in a nice way,
> but mainly because I don't want commands to accidentally skip some
> previous implementation by accident.  For instance, if we had an
> invoke_original method, then DeleteCommand2 could call that and skip
> DeleteCommand1.

I too have sometimes wondered about providing override type
behaviour.

I wonder, instead of having 'invoke_overridden' and/or
'invoke_original', if we could/should look to TCL for inspiration,
specifically it's uplevel command[1].

If we consider your 'delete' example, the builtin delete would be
considered to be at level 0, then we define DeleteCommand1, this would
be level 1, and then comes DeleteCommand2 which would be at level 2.

Inside DeleteCommand1 we can do:

  DeleteCommand1.invoke_uplevel (-1, args)

this would invoke the command one level down the stack, so in this
case the builtin delete.  In DeleteCommand2 we can do:

  DeleteCommand2.invoke_uplevel (-1, args)

again, this invokes the command one level down the stack, so in this
case DeleteCommand1.

However, we could also use:

  DeleteCommand2.invoke_uplevel (0, args)

In this case the level argument 0 indicates we want to directly access
the instance of the command at level 0.

Where I would find this idea really interesting is that we could
easily provide access to this functionality from the command line.
This might be useful in two ways I think, first, if a Python script
has overridden a command and a user wants to quickly bypass that for
some reason they can just do:

  (gdb) uplevel 0 delete args...

in order to get the base version of the command.  Second, with some
extra work we could make the existing hook mechanism redundant, and
possibly provide something more flexible (maybe?), consider:

  define delete
    echo Basically a pre-hook here\n
    uplevel -1 delete $argv
    echo Basically a post-hook here\n
  end

Notice I just invented '$argv' on the fly there - this doesn't exist
currently, but I'm imagining that this would expand to all of the
arguments passed to the newly defined 'delete' command.

Anyway, I don't think all of the ideas mentioned above would need to
be implemented in one go, but I thought it might be worth mentioning
some of the thoughts I'd had in this area.

Thanks,
Andrew

[1] https://www.tcl.tk/man/tcl8.4/TclCmd/uplevel.htm
  
Marco Barisione Nov. 6, 2019, 8:42 a.m. UTC | #5
On 5 Nov 2019, at 10:17, Andrew Burgess <andrew.burgess@embecosm.com> wrote:
> * Marco Barisione <mbarisione@undo.io> [2019-11-01 21:01:18 +0000]:
>> Let's say you override delete. Let's call the first class overriding it
>> DeleteCommand1 and the second DeleteCommand2.
>> 
>> If DeleteCommand1 didn't use preserve_when_overridden then
>> DeleteCommand2.invoke_overridden will just call the original delete.
>> 
>> If DeleteCommand1 was preserved, then DeleteCommand2.invoke_overridden
>> will call DeleteCommand1.invoke.  At this point DeleteCommand1 may just do
>> whatever it needs on its own, or call its own invoke_overridden method
>> which will call the original delete command.
>> DeleteCommand2 has not way to invoke the original delete command directly.
>> This is partly because I'm not sure how I would expose this in a nice way,
>> but mainly because I don't want commands to accidentally skip some
>> previous implementation by accident.  For instance, if we had an
>> invoke_original method, then DeleteCommand2 could call that and skip
>> DeleteCommand1.
> 
> I too have sometimes wondered about providing override type
> behaviour.
> 
> I wonder, instead of having 'invoke_overridden' and/or
> 'invoke_original', if we could/should look to TCL for inspiration,
> specifically it's uplevel command[1].

Hm, I think this is potentially a very good idea.
I’m slightly worried about people accidentally (or on purpose but without
understanding the consequences) skipping a new command implementation,
but it’s probably not a big deal.

I would suggest the method to be uplevel_command(args, from_tty[, level])
so, if you don't specify the level, you just invoke the directly
overridden command.

> If we consider your 'delete' example, the builtin delete would be
> considered to be at level 0, then we define DeleteCommand1, this would
> be level 1, and then comes DeleteCommand2 which would be at level 2.
> 
> Inside DeleteCommand1 we can do:
> 
>  DeleteCommand1.invoke_uplevel (-1, args)
> 
> this would invoke the command one level down the stack, so in this
> case the builtin delete.  In DeleteCommand2 we can do:

I would say this is level 1, not level -1.  This would match TCL, make
more sense with the name (IMHO) of the method as it says "up”, and make
the common case use positive numbers, not negative ones.

Does it make sense to have a level of 0?  Why not just calling the invoke
method directly?

How about negative numbers?  Why would you need it?  It seems it's risky
as you could easily create cycles.
It would also mean that an overridden command would need to know that it
was overridden and that the overriding command if not going to blindly
call back the overridden command.

Because of this, I *think* it would make more sense (at least in the first
version of this patch until we find a use case) to just have level > 0 (or
>= 0).

> Where I would find this idea really interesting is that we could
> easily provide access to this functionality from the command line.
> This might be useful in two ways I think, first, if a Python script
> has overridden a command and a user wants to quickly bypass that for
> some reason they can just do:
> 
>  (gdb) uplevel 0 delete args…

I love this!
Why 0?  Shouldn’t this be -1 following your example above?

Like I said above I think this would be simpler if level were > 0 and
optional:
    uplevel delete 42
Or:
    uplevel 1 delete 42

> in order to get the base version of the command.  Second, with some
> extra work we could make the existing hook mechanism redundant, and
> possibly provide something more flexible (maybe?), consider:
> 
>  define delete
>    echo Basically a pre-hook here\n
>    uplevel -1 delete $argv
>    echo Basically a post-hook here\n
>  end

Being able to avoid hooks is one of the reasons for this patch.
Currently, in my code, I have a lot of hooks which just call into Python
but they are ugly (GDB Script code to just call into Python) and they
cannot prevent the command from being executed without a Python stacktrace
being printed (ont very nice for users).

I'd be happy to provide uplevel as part of this patch.

> Notice I just invented '$argv' on the fly there - this doesn't exist
> currently, but I'm imagining that this would expand to all of the
> arguments passed to the newly defined 'delete' command.
> 
> Anyway, I don't think all of the ideas mentioned above would need to
> be implemented in one go, but I thought it might be worth mentioning
> some of the thoughts I'd had in this area.

I’d skip the $argv thing for this patch but I think it makes sense.

How would the from_tty argument for C or Python command be defined?
  
Pedro Alves Nov. 6, 2019, 4 p.m. UTC | #6
On 11/5/19 10:17 AM, Andrew Burgess wrote:
> in order to get the base version of the command.  Second, with some
> extra work we could make the existing hook mechanism redundant, and
> possibly provide something more flexible (maybe?), consider:
> 
>   define delete
>     echo Basically a pre-hook here\n
>     uplevel -1 delete $argv
>     echo Basically a post-hook here\n
>   end
> 
> Notice I just invented '$argv' on the fly there - this doesn't exist
> currently, but I'm imagining that this would expand to all of the
> arguments passed to the newly defined 'delete' command.

Something like "$@" would be better than $argv, because the latter
implies that arguments are split into an "argument vector", which doesn't
work correctly in general because different commands split arguments
in different forms.

Thanks,
Pedro Alves
  
Marco Barisione Nov. 7, 2019, 10:22 a.m. UTC | #7
On 6 Nov 2019, at 08:42, Marco Barisione <mbarisione@undo.io> wrote:
>> I wonder, instead of having 'invoke_overridden' and/or
>> 'invoke_original', if we could/should look to TCL for inspiration,
>> specifically it's uplevel command[1].
> 
> Hm, I think this is potentially a very good idea.

I thought more about this and I’m not sure that uplevel would be
clear to somebody who never touched TCL before.
I don’t have any better suggestion at the moment. Ideas?
  

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 59895bd68b..761c32cee0 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -83,6 +83,14 @@ 
   ** gdb.Block now supports the dictionary syntax for accessing symbols in
      this block (e.g. block['local_variable']).
 
+  ** gdb.Command has a new method 'invoke_overridden' to invoke the
+     command with the same name, if any, which was overridden by the new
+     command.  gdb.Command.__init__ has a new 'preserve_when_overridden'
+     argument which, if true (the default is false), means that the
+     command, once overridden by another command of the same name, can
+     still be invoked through the new command's 'invoke_overridden'
+     method.
+
 * New commands
 
 | [COMMAND] | SHELL_COMMAND
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 7ace72fb7e..e3fe9aa26c 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -30,12 +30,14 @@ 
 
 static void undef_cmd_error (const char *, const char *);
 
-static struct cmd_list_element *delete_cmd (const char *name,
-					    struct cmd_list_element **list,
-					    struct cmd_list_element **prehook,
-					    struct cmd_list_element **prehookee,
-					    struct cmd_list_element **posthook,
-					    struct cmd_list_element **posthookee);
+static struct cmd_list_element *remove_cmd (
+  const char *name,
+  struct cmd_list_element **list,
+  struct cmd_list_element **preserved_overridden_cmd,
+  struct cmd_list_element **prehook,
+  struct cmd_list_element **prehookee,
+  struct cmd_list_element **posthook,
+  struct cmd_list_element **posthookee);
 
 static struct cmd_list_element *find_cmd (const char *command,
 					  int len,
@@ -198,10 +200,12 @@  do_add_cmd (const char *name, enum command_class theclass,
 							    doc);
   struct cmd_list_element *p, *iter;
 
+  struct cmd_list_element *preserved_overridden_cmd;
   /* Turn each alias of the old command into an alias of the new
      command.  */
-  c->aliases = delete_cmd (name, list, &c->hook_pre, &c->hookee_pre,
-			   &c->hook_post, &c->hookee_post);
+  c->aliases = remove_cmd (name, list, &preserved_overridden_cmd,
+			   &c->hook_pre, &c->hookee_pre, &c->hook_post,
+			   &c->hookee_post);
   for (iter = c->aliases; iter; iter = iter->alias_chain)
     iter->cmd_pointer = c;
   if (c->hook_pre)
@@ -212,6 +216,7 @@  do_add_cmd (const char *name, enum command_class theclass,
     c->hook_post->hookee_post = c;
   if (c->hookee_post)
     c->hookee_post->hook_post = c;
+  c->preserved_overridden_cmd = preserved_overridden_cmd;
 
   if (*list == NULL || strcmp ((*list)->name, name) >= 0)
     {
@@ -300,14 +305,15 @@  add_alias_cmd (const char *name, cmd_list_element *old,
 {
   if (old == 0)
     {
+      struct cmd_list_element *preserved_overridden_cmd;
       struct cmd_list_element *prehook, *prehookee, *posthook, *posthookee;
-      struct cmd_list_element *aliases = delete_cmd (name, list,
-						     &prehook, &prehookee,
-						     &posthook, &posthookee);
+      struct cmd_list_element *aliases =
+	remove_cmd (name, list, &preserved_overridden_cmd,
+		    &prehook, &prehookee, &posthook, &posthookee);
 
       /* If this happens, it means a programmer error somewhere.  */
-      gdb_assert (!aliases && !prehook && !prehookee
-		  && !posthook && ! posthookee);
+      gdb_assert (!aliases && !preserved_overridden_cmd && !prehook &&
+		  !prehookee && !posthook && ! posthookee);
       return 0;
     }
 
@@ -840,13 +846,25 @@  add_setshow_zuinteger_cmd (const char *name, enum command_class theclass,
 
 /* Remove the command named NAME from the command list.  Return the
    list commands which were aliased to the deleted command.  If the
-   command had no aliases, return NULL.  The various *HOOKs are set to
-   the pre- and post-hook commands for the deleted command.  If the
-   command does not have a hook, the corresponding out parameter is
+   command had no aliases, return NULL.
+
+   *PRESERVED_OVERRIDDEN_CMD is set to a command with the same name
+   which was overridden, but can still be called from the new command
+   implementation (that is, its preserve_when_overridden field is
+   true).  This is not necessarily the command which was removed.
+   For instance, if the command itself has preserved_overridden_cmd
+   set to false, but a previous overridden command has it set to
+   true.  If no previosuly overridden command still exists, the
+   corresponding out parameter is set to NULL.
+
+   The various *HOOKs are set to the pre- and post-hook commands for
+   the deleted command.  If the command does not have a hook, the
+   corresponding out parameter is
    set to NULL.  */
 
 static struct cmd_list_element *
-delete_cmd (const char *name, struct cmd_list_element **list,
+remove_cmd (const char *name, struct cmd_list_element **list,
+	    struct cmd_list_element **preserved_overridden_cmd,
 	    struct cmd_list_element **prehook,
 	    struct cmd_list_element **prehookee,
 	    struct cmd_list_element **posthook,
@@ -856,6 +874,7 @@  delete_cmd (const char *name, struct cmd_list_element **list,
   struct cmd_list_element **previous_chain_ptr;
   struct cmd_list_element *aliases = NULL;
 
+  *preserved_overridden_cmd = NULL;
   *prehook = NULL;
   *prehookee = NULL;
   *posthook = NULL;
@@ -866,8 +885,23 @@  delete_cmd (const char *name, struct cmd_list_element **list,
     {
       if (strcmp (iter->name, name) == 0)
 	{
-	  if (iter->destroyer)
-	    iter->destroyer (iter, iter->context);
+	  if (iter->preserve_when_overridden)
+	    *preserved_overridden_cmd = iter;
+	  else
+	    {
+	      /* Even if this item cannot be preserved when overridden, it
+		 may have overridden a command which was preserved.  */
+	      *preserved_overridden_cmd = iter->preserved_overridden_cmd;
+	      /* If the overridden command was not removed and freed, then
+		 it must have preserve_when_overridden be true.  This
+		 also means we don't need to recurse further.  */
+	      gdb_assert (
+	        *preserved_overridden_cmd == nullptr
+		|| (*preserved_overridden_cmd)->preserve_when_overridden);
+	      if (iter->destroyer)
+		iter->destroyer (iter, iter->context);
+	    }
+
 	  if (iter->hookee_pre)
 	    iter->hookee_pre->hook_pre = 0;
 	  *prehook = iter->hook_pre;
@@ -897,7 +931,17 @@  delete_cmd (const char *name, struct cmd_list_element **list,
 	      *prevp = iter->alias_chain;
 	    }
 
-	  delete iter;
+	  if (iter->preserve_when_overridden)
+	    {
+	      /* We preserved this command so we just need to break its
+		 part of the linked list, but not free it.  */
+	      iter->next = nullptr;
+	      gdb_assert (iter == *preserved_overridden_cmd);
+	    }
+	  else
+	    /* If the command cannot be preserved, we already called the
+	       destroyer and this command needs to be deleted.  */
+	    delete iter;
 
 	  /* We won't see another command with the same name.  */
 	  break;
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 2ec4a97d81..8826922590 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -217,6 +217,16 @@  struct cmd_list_element
     /* Pointer to command strings of user-defined commands */
     counted_command_line user_commands;
 
+    /* Whether this command, once overridden by another command, can
+       still be called by the implementation of the new command.  */
+    bool preserve_when_overridden = true;
+
+    /* Pointer to command that was overridden by this command (and that
+       has preserve_when_overridden == true).
+       This command is kept around as it can still be invoked by the
+       overriding command.  */
+    struct cmd_list_element *preserved_overridden_cmd = nullptr;
+
     /* Pointer to command that is hooked by this one, (by hook_pre)
        so the hook can be removed when this one is deleted.  */
     struct cmd_list_element *hookee_pre = nullptr;
diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index 8abd48c678..ea07ef7f36 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -1448,6 +1448,9 @@  do_define_command (const char *comname, int from_tty,
 		  ? c->doc : xstrdup ("User-defined."), list);
   newc->user_commands = std::move (cmds);
 
+  /* Commands defined by the user are never preserved once overridden.  */
+  newc->preserve_when_overridden = false;
+
   /* If this new command is a hook, then mark both commands as being
      tied.  */
   if (hookc)
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 13191fc9ae..063ae1a200 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -3617,7 +3617,7 @@  You can implement new @value{GDBN} CLI commands in Python.  A CLI
 command is implemented using an instance of the @code{gdb.Command}
 class, most commonly using a subclass.
 
-@defun Command.__init__ (name, @var{command_class} @r{[}, @var{completer_class} @r{[}, @var{prefix}@r{]]})
+@defun Command.__init__ (name, @var{command_class} @r{[}, @var{completer_class} @r{[}, @var{prefix} @r{[}, @var{preserve_when_overridden}@r{]]]})
 The object initializer for @code{Command} registers the new command
 with @value{GDBN}.  This initializer is normally invoked from the
 subclass' own @code{__init__} method.
@@ -3644,6 +3644,13 @@  error will occur when completion is attempted.
 command is a prefix command; sub-commands of this command may be
 registered.
 
+@var{preserve_when_overridden} is an optional argument.  If @code{True},
+then the new command is preserved when it's overridden by another
+command with the same name, otherwise it's not preserved (potentially
+causing the instance to be freed, unless your code holds a reference to
+it).  When a command is preserved, the overriding command can still
+invoke it through its @code{invoke_overridden} method.
+
 The help text for the new command is taken from the Python
 documentation string for the command's class, if there is one.  If no
 documentation string is provided, the default value ``This command is
@@ -3686,6 +3693,30 @@  print gdb.string_to_argv ("1 2\ \\\"3 '4 \"5' \"6 '7\"")
 
 @end defun
 
+@defun Command.invoke_overridden (argument, from_tty)
+If the command overrides another command of the same name, the
+implementation of the original command can be invoked through this
+method as long as the overridden command supports this feature:
+
+@itemize @bullet
+@item
+All the commands defined by @value{GDBN} itself support being invoked
+when overridden.
+
+@item
+Commands defined through the @code{define} command are never preserved
+when overridden.
+
+@item
+Commands defined through @code{gdb.Command} are preserved only if they
+set the @code{preserve_when_overridden} argument to the @code{__init__}
+method to @code{true}.
+@end itemize
+
+If there's no overridden and preserved command, this method will raise a
+@code{gdb.error}.
+@end defun
+
 @cindex completion of Python commands
 @defun Command.complete (text, word)
 This method is called by @value{GDBN} when the user attempts
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index 87d1888c52..46ff1eeb3f 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -417,6 +417,38 @@  gdbpy_parse_command_name (const char *name,
   return NULL;
 }
 
+/* Python function which invokes the command overwritten by self.  */
+static PyObject *
+cmdpy_invoke_overridden (PyObject *self, PyObject *py_args, PyObject *kw)
+{
+  static const char *keywords[] = { "args", "from_tty", NULL };
+
+  const char *args = NULL;
+  PyObject *from_tty_obj = NULL;
+  if (!gdb_PyArg_ParseTupleAndKeywords (py_args, kw,
+					"sO",
+					keywords,
+					&args,
+					&from_tty_obj))
+    return NULL;
+
+  cmdpy_object *obj = (cmdpy_object *) self;
+  cmd_list_element *preserved_overridden_cmd =
+    obj->command->preserved_overridden_cmd;
+  if (preserved_overridden_cmd == nullptr)
+    {
+      PyErr_Format (gdbpy_gdb_error,
+		    _("The '%s' command did not override any other command"),
+		    obj->command->name);
+      return NULL;
+    }
+
+  int from_tty = PyObject_IsTrue (from_tty_obj);
+  cmd_func (preserved_overridden_cmd, args, from_tty);
+
+  Py_RETURN_NONE;
+}
+
 /* Object initializer; sets up gdb-side structures for command.
 
    Use: __init__(NAME, COMMAND_CLASS [, COMPLETER_CLASS][, PREFIX]]).
@@ -448,8 +480,10 @@  cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
   struct cmd_list_element **cmd_list;
   char *cmd_name, *pfx_name;
   static const char *keywords[] = { "name", "command_class", "completer_class",
-				    "prefix", NULL };
+				    "prefix", "preserve_when_overridden",
+				    NULL };
   PyObject *is_prefix = NULL;
+  PyObject *preserve_when_overridden_obj = NULL;
   int cmp;
 
   if (obj->command)
@@ -461,9 +495,10 @@  cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
       return -1;
     }
 
-  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "si|iO",
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "si|iOO",
 					keywords, &name, &cmdtype,
-					&completetype, &is_prefix))
+					&completetype, &is_prefix,
+					&preserve_when_overridden_obj))
     return -1;
 
   if (cmdtype != no_class && cmdtype != class_run
@@ -521,6 +556,14 @@  cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 	  return -1;
 	}
     }
+
+  bool preserve_when_overridden = false;
+  if (preserve_when_overridden_obj != nullptr
+      && PyObject_IsTrue (preserve_when_overridden_obj))
+    {
+      preserve_when_overridden = true;
+    }
+
   if (PyObject_HasAttr (self, gdbpy_doc_cst))
     {
       gdbpy_ref<> ds_obj (PyObject_GetAttr (self, gdbpy_doc_cst));
@@ -563,6 +606,7 @@  cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
       /* There appears to be no API to set this.  */
       cmd->func = cmdpy_function;
       cmd->destroyer = cmdpy_destroyer;
+      cmd->preserve_when_overridden = preserve_when_overridden;
 
       obj->command = cmd;
       set_cmd_context (cmd, self_ref.release ());
@@ -644,6 +688,10 @@  static PyMethodDef cmdpy_object_methods[] =
 {
   { "dont_repeat", cmdpy_dont_repeat, METH_NOARGS,
     "Prevent command repetition when user enters empty line." },
+  { "invoke_overridden", (PyCFunction) cmdpy_invoke_overridden,
+    METH_VARARGS | METH_KEYWORDS,
+    "invoke_overridden (args, from_tty)\n\
+Invoke the command which was overridden by this command." },
 
   { 0 }
 };
diff --git a/gdb/testsuite/gdb.python/py-chain-invoke.exp b/gdb/testsuite/gdb.python/py-chain-invoke.exp
new file mode 100644
index 0000000000..bd07f58592
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-chain-invoke.exp
@@ -0,0 +1,315 @@ 
+# Copyright (C) 2009-2019 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 3 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, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests accessing overridden
+# commands.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+# Prepare for testing.
+#
+# This quits GDB (if running), starts a new one, and loads any required
+# external scripts.
+
+proc prepare_gdb {} {
+  global srcdir subdir testfile
+
+  gdb_exit
+  gdb_start
+  gdb_reinitialize_dir $srcdir/$subdir
+
+  # Skip all tests if Python scripting is not enabled.
+  if { [skip_python_tests] } { continue }
+
+  gdb_test_no_output "set confirm off"
+
+  # Load the code which adds commands.
+  set remote_python_file \
+    [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+  gdb_test_no_output "source ${remote_python_file}" "load python file"
+}
+
+# Add a command called CMD through the Python API.
+#
+# The command will print messages including the string MSG when invoked.
+#
+# If PRESERVE_WHEN_OVERRIDDEN is 1, the command is not freed when
+# overridden and can be invoked by the overriding command though the
+# invoke_overridden method.
+
+proc add_py_cmd { cmd msg preserve_when_overridden } {
+  gdb_test_no_output \
+    "python TestCommand ('${cmd}', '${msg}', ${preserve_when_overridden})"
+}
+
+# Add a command called CMD through GDB scripting.
+#
+# The command will print messages including the string MSG when invoked.
+
+proc add_gdb_script_cmd { cmd msg } {
+  gdb_define_cmd $cmd [list "echo gdb: ${msg}\\n"]
+}
+
+# Define an alias.
+
+proc define_alias { alias_name original_name } {
+  gdb_test_no_output "alias ${alias_name} = ${original_name}"
+}
+
+# test_py_commands_deleted [MSG1 [MSG2 [...]]]
+# Check that the Python class instances for the commands with the
+# specified messages were deleted.
+#
+# The specified commands must have been added with add_py_cmd passing
+# 0 as preserve_when_overridden, meaning that the instance became useless
+# (and thus freed) once overridden.
+
+proc test_py_commands_deleted { args } {
+  # Quote the strings.
+  set expected_quoted {}
+  foreach value $args {
+    lappend expected_quoted "'${value}'"
+  }
+  # Add commas to separated elements (to make it into the content of a
+  # Pyyhon list.
+  set py_args [join $expected_quoted ", "]
+  # Check that the arguments we got (and made into a list) correspond
+  # to what was actually deleted.
+  gdb_test_no_output \
+    "python check_were_deleted (\[${py_args}\])" "check commands deleted"
+}
+
+# test_sequence_exact CMD LIST
+# Like gdb_test_exact but, for convenience, it accepts a list of lines
+# instead of a single line.
+proc test_sequence_exact { cmd lines } {
+  set expected_string [join $lines "\n"]
+  gdb_test_exact $cmd $expected_string
+}
+
+proc cs { preserve_when_overridden } {
+  if { $preserve_when_overridden } {
+    return "preserved Python command"
+  } else {
+    return "non-preserved Python command"
+  }
+}
+
+proc test_override_delete_with_py { py_preserve_when_overridden } {
+  with_test_prefix "override delete with [cs $py_preserve_when_overridden]" {
+    prepare_gdb
+    define_alias "new-delete" "del"
+    add_py_cmd "delete" "new delete" $py_preserve_when_overridden
+
+    set expected_list {
+      "py-before: new delete"
+      "No breakpoint number 9999."
+      "py-after: new delete"
+    }
+
+    test_sequence_exact "delete 9999" $expected_list
+    test_sequence_exact "del 9999" $expected_list
+    test_sequence_exact "new-delete 9999" $expected_list
+
+    # No custom Python command should have been overridden.
+    test_py_commands_deleted
+  }
+}
+
+proc test_gdb_script_overridden_by_py_cmd { py_preserve_when_overridden } {
+  with_test_prefix "override a defined command with\
+		    [cs $py_preserve_when_overridden]" {
+    prepare_gdb
+    add_gdb_script_cmd "new-command" "new command"
+    define_alias "new-alias" "new-command"
+    add_py_cmd "new-command" "new command" $py_preserve_when_overridden
+
+    set expected_list {
+      "py-before: new command"
+      "py-no-overridden: new command"
+      "py-after: new command"
+    }
+
+    test_sequence_exact "new-command" $expected_list
+    test_sequence_exact "new-alias" $expected_list
+
+    # No custom Python command should have been overridden.
+    test_py_commands_deleted
+  }
+}
+
+proc test_non_preserve_py_cmd_overridden_by_py {
+	second_preserve_when_overridden } {
+  with_test_prefix "override a [cs 0] with\
+		    [cs $second_preserve_when_overridden]" {
+    prepare_gdb
+    add_py_cmd "new-command" "new command 1" 0
+    add_py_cmd "new-command" "new command 2" $second_preserve_when_overridden
+
+    test_sequence_exact "new-command" {
+      "py-before: new command 2"
+      "py-no-overridden: new command 2"
+      "py-after: new command 2"
+    }
+
+    # The first Python command was overridden and, as its functionality
+    # is not preserved when overridden, it was deleted.
+    # The second Python command was not overridden so it should never
+    # be deleted.
+    test_py_commands_deleted "new command 1"
+  }
+}
+
+proc test_preserve_py_cmd_overridden_by_py {
+	second_preserve_when_overridden } {
+  with_test_prefix "override [cs 1] with\
+		    [cs $second_preserve_when_overridden]" {
+    prepare_gdb
+    add_py_cmd "new-command" "new command 1" 1
+    add_py_cmd "new-command" "new command 2" $second_preserve_when_overridden
+
+    test_sequence_exact "new-command" {
+      "py-before: new command 2"
+      "py-before: new command 1"
+      "py-no-overridden: new command 1"
+      "py-after: new command 1"
+      "py-after: new command 2"
+    }
+
+    # The first Python command was overridden and, as its functionality
+    # is preserved when overridden, it was not deleted.
+    # The second Python command was not overridden so it should never
+    # be deleted.
+    test_py_commands_deleted
+  }
+}
+
+proc test_py_cmd_overridden_by_gdb_script { py_preserve_when_overridden } {
+  with_test_prefix "override a [cs $py_preserve_when_overridden] with a\
+		    defined one" {
+    prepare_gdb
+    add_py_cmd "new-command" "new command" $py_preserve_when_overridden
+    add_gdb_script_cmd "new-command" "new GDB script command"
+
+    test_sequence_exact "new-command" {
+      "gdb: new GDB script command"
+    }
+
+    if { $py_preserve_when_overridden } {
+      # The Python command was overridden but, as it can be preserved,
+      # it was not deleted (even if it's not accessible from the GDB
+      # script).
+      test_py_commands_deleted
+    } else {
+      # The Python command was overridden and, as it cannot be
+      # preserved, it deleted.
+      test_py_commands_deleted "new command"
+    }
+  }
+}
+
+proc test_preserve_py_cmd_gdb_script_py_cmd {
+	second_py_preserve_when_overridden } {
+  with_test_prefix "override a [cs 1] with a defined one with a\
+	            [cs $second_py_preserve_when_overridden]" {
+    prepare_gdb
+    add_py_cmd "new-command" "new py command 1" 1
+    add_gdb_script_cmd "new-command" "new GDB script command"
+    add_py_cmd "new-command" "new py command 2"\
+	       $second_py_preserve_when_overridden
+
+    # The GDB script command should not be preserved, but both Python
+    # ones should.
+    test_sequence_exact "new-command" {
+      "py-before: new py command 2"
+      "py-before: new py command 1"
+      "py-no-overridden: new py command 1"
+      "py-after: new py command 1"
+      "py-after: new py command 2"
+    }
+
+    # The first Python command was overridden and, as its functionality
+    # is preserved when overridden, it was not deleted.
+    test_py_commands_deleted
+  }
+}
+
+proc test_override_many { later_preserve_when_overridden } {
+  with_test_prefix "override delete with many commands, with a top level\
+                    [cs $later_preserve_when_overridden]" {
+    prepare_gdb
+    define_alias "new-delete-defined-early" "delete"
+    add_py_cmd "delete" "py delete 1" 0
+    add_py_cmd "delete" "py delete 2" 1
+    add_py_cmd "delete" "py delete 3" 1
+    add_py_cmd "delete" "py delete 4" 0
+    define_alias "new-delete-defined-middle" "delete"
+    add_py_cmd "delete" "py delete 5" 1
+    add_gdb_script_cmd "delete" "GDB delete 1"
+    add_py_cmd "delete" "py delete 6" 0
+    add_gdb_script_cmd "delete" "GDB delete 2"
+    add_py_cmd "delete" "py delete 7" $later_preserve_when_overridden
+    define_alias "new-delete-defined-late" "delete"
+
+    set expected_list {
+      "py-before: py delete 7"
+      "py-before: py delete 5"
+      "py-before: py delete 3"
+      "py-before: py delete 2"
+      "No breakpoint number 9999."
+      "py-after: py delete 2"
+      "py-after: py delete 3"
+      "py-after: py delete 5"
+      "py-after: py delete 7"
+    }
+
+    # Long command form.
+    test_sequence_exact "delete 9999" $expected_list
+
+    # Short command form.
+    test_sequence_exact "del 9999" $expected_list
+
+    # Aliases.
+    # There are three aliases: one defined before any command, one after
+    # some commands are defined, and one defined after all the commands
+    # were defined.  When they were defined should be irrelevant and they
+    # should all work the same.
+    test_sequence_exact "new-delete-defined-early 9999" $expected_list
+    test_sequence_exact "new-delete-defined-middle 9999" $expected_list
+    test_sequence_exact "new-delete-defined-late 9999" $expected_list
+
+    test_py_commands_deleted "py delete 1" "py delete 4" "py delete 6"
+  }
+}
+
+set current_lang "c"
+
+with_test_prefix "overriding commands" {
+  # All the test commands accept a 0/1 argument which control whether the
+  # latest defined command should be preserved when overridden.  The
+  # status of this command should not affect the result, so we try with
+  # both combinations.
+  foreach arg { 0 1 } {
+    test_override_delete_with_py $arg
+    test_gdb_script_overridden_by_py_cmd $arg
+    test_non_preserve_py_cmd_overridden_by_py $arg
+    test_preserve_py_cmd_overridden_by_py $arg
+    test_py_cmd_overridden_by_gdb_script $arg
+    test_preserve_py_cmd_gdb_script_py_cmd $arg
+    test_override_many $arg
+  }
+}
diff --git a/gdb/testsuite/gdb.python/py-chain-invoke.py b/gdb/testsuite/gdb.python/py-chain-invoke.py
new file mode 100644
index 0000000000..e72357af16
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-chain-invoke.py
@@ -0,0 +1,72 @@ 
+# Copyright (C) 2008-2019 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 3 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, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests the
+# gdb.Command.invoke_overridden method.
+
+import gc
+import sys
+import textwrap
+
+import gdb
+
+deleted = []
+
+class TestCommand (gdb.Command):
+
+    def __init__ (self, cmd_name, msg, preserve_when_overridden):
+        self._cmd_name = cmd_name
+        self._msg = msg
+
+        gdb.Command.__init__ (
+            self, cmd_name, gdb.COMMAND_NONE,
+            preserve_when_overridden=preserve_when_overridden)
+
+    def __del__ (self):
+        deleted.append (self._msg)
+
+    def invoke (self, args, from_tty):
+        print ('py-before: %s' % self._msg)
+        try:
+            self.invoke_overridden (args, from_tty)
+        except gdb.error as exc:
+            expected_exc_msg = (
+                "The '%s' command did not override any other command" %
+                self._cmd_name)
+            if str (exc) == expected_exc_msg:
+                print ('py-no-overridden: %s' % self._msg)
+            else:
+                raise
+        print ('py-after: %s' % self._msg)
+
+def check_were_deleted (expected):
+    # Once GDB drops a reference to the gdb.Command instance, the object
+    # should be deleted.  Just to be sure, we can force a collection.
+    gc.collect ()
+
+    expected.sort ()
+    deleted.sort ()
+
+    if expected != deleted:
+        print (textwrap.dedent ('''\
+            Mismatch in the commands which were deleted:
+            - Actually deleted commands (%d): %r
+            - Expected deleted commands (%d): %r\
+            ''' % (
+                len (deleted),
+                deleted,
+                len (expected),
+                expected
+                )))