This is the mail archive of the gdb-patches@sourceware.cygnus.com 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]

Re: [PATCH] Running the inferior from breakpoint commands


Eli Zaretskii wrote:
> 
> This is about the problem described in the following message:
> 
>   http://sourceware.cygnus.com/ml/gdb/2000-q1/msg00684.html
> 
> The relevant test cases are in gdb/testsuite/gdb.base/commands.exp.

Eli, 

While this is intriguing, I don't think it's as simple as
you suppose.  In the first place, it is a documented limitation
that GDB only partially handles breakpoint command lists if they
contain commands that run the target.  The GDB manual states:

	You can use breakpoint commands to start your program
	up again.  Simply use the continue command, or step,
	or any other command that resumes execution.  Any
	other commands in the command list are ignored, after
	a command that resumes execution.

Note the last sentence.  So despite the fact that the 
testsuite seems to imply that this should work, it is
not expected to.  I have no idea what that test is 
doing in there.  The commands-on-breakpoint behavior
is NOT intended to be recursive.

Therefore what you are trying to do is more of an 
enhancement than a bug fix.  As such, I would need to
see some new tests; and given the difficulty of solving
this problem correctly, they would have to be pretty
thorough.  I would have to see tests for every kind of
execution command (step, next, call, finish, return, etc.)
combined with a wide variety of "things that could go
wrong (expression evaluation using globals, locals, 
recursive vs. non-recursive functions, multiple 
breakpoints on mutually recursive functions...)

If you seriously want to undertake this project, we can
work on a list of criteria that should be tested (things
that can go wrong).  Off the top of my head, I can think
of:

    * What happens if we continue and hit another BP?
    * What happens if we continue and hit the same
      BP, eg. in a recursive function?
    * What happens if we continue and switch threads?
    * What happens if we continue and the program terminates?
    * What happens if we continue, hit another BP, 
      then that BP continues and we hit the first BP?
    * What happens if the program isn't continuable?

On top of that, I have grave concerns about being able
to correctly save and restore all of the internal
debugger state necessary to make this work (the
infrun execution state, the expression chain, etc.)
Not to discourage you from trying it, but I would be 
very suspicious of any effort that only took a few days
to complete.

				Sincerely,
				Michael Snyder

> Here are the patches which correct the problems I described in the
> original message.  After applying them, the test program works as I'd
> expect (and as commands.exp seems to want).
> 
> I didn't see any replies to my message, so I still don't know whether
> the original code works for other platforms (I think it shouldn't).
> Could someone please try commands.exp and tell what you get?
> 
> Please tell if it's okay to commit these changes.  If they are
> accepted, I will also send changes for the docs, since I think there
> are some limitations on what breakpoint commands can do that should be
> documented.
> 
> 2000-03-14  Eli Zaretskii  <eliz@is.elta.co.il>
> 
>         * breakpoint.c (breakpoint_alive_p): New function.
>         (bpstat_do_actions): Allow recursive invocation, provided that the
>         argument isn't identical to the one last seen.  Stop executing
>         commands if their breakpoint no longer exists (e.g., was deleted
>         by the previous command).  Save and restore the bpstat chain if
>         the command proceeded the inferior.  Invoke bpstat_do_actions
>         recursively to process the new bpstat produced when the proceeding
>         inferior stops.
> 
> --- gdb/breakpoint.c~3  Wed Mar  8 20:02:20 2000
> +++ gdb/breakpoint.c    Tue Mar 14 18:17:50 2000
> @@ -1820,6 +1820,20 @@ bpstat_clear_actions (bs)
>      }
>  }
> 
> +/* Is the breakpoint BPT still defined?  */
> +static int
> +breakpoint_alive_p (bpt)
> +     struct breakpoint *bpt;
> +{
> +  struct breakpoint *b;
> +
> +  ALL_BREAKPOINTS (b)
> +    if (b == bpt)
> +      return 1;
> +
> +  return 0;
> +}
> +
>  /* Stub for cleaning up our state if we error-out of a breakpoint command */
>  /* ARGSUSED */
>  static void
> @@ -1838,19 +1852,23 @@ void
>  bpstat_do_actions (bsp)
>       bpstat *bsp;
>  {
> -  bpstat bs;
> +  static bpstat last_bpstat;
> +  bpstat bs, saved_bs;
>    struct cleanup *old_chain;
>    struct command_line *cmd;
> +  int bs_idx, cmd_idx;
> 
>    /* Avoid endless recursion if a `source' command is contained
>       in bs->commands.  */
> -  if (executing_breakpoint_commands)
> +  if (executing_breakpoint_commands
> +      && memcmp (bsp, &last_bpstat, sizeof (*bsp)) == 0)
>      return;
> 
> +  last_bpstat = *bsp;
> +
>    executing_breakpoint_commands = 1;
>    old_chain = make_cleanup (cleanup_executing_breakpoints, 0);
> 
> -top:
>    /* Note that (as of this writing), our callers all appear to
>       be passing us the address of global stop_bpstat.  And, if
>       our calls to execute_control_command cause the inferior to
> @@ -1863,26 +1881,78 @@ top:
>    bs = *bsp;
> 
>    breakpoint_proceeded = 0;
> -  for (; bs != NULL; bs = bs->next)
> +  for (bs_idx = 0; bs != NULL; bs = bs->next, bs_idx++)
>      {
> +      /* If someone deleted the breakpoint associated with this
> +        bpstat, we cannot run its commands, since deleting a
> +        breakpoint nukes its command lines.  */
> +      if (!breakpoint_alive_p (bs->breakpoint_at))
> +       continue;
>        cmd = bs->commands;
> +      cmd_idx = 0;
>        while (cmd != NULL)
>         {
> +         struct cleanup *pchain;
> +         int idx;
> +
> +         saved_bs = bpstat_copy (*bsp);
> +         pchain = make_cleanup ((make_cleanup_func)bpstat_clear, &saved_bs);
>           execute_control_command (cmd);
> 
>           if (breakpoint_proceeded)
> +           {
> +             /* The inferior is proceeded by the command.  We cannot
> +                continue, as the bpstat chain has been blown away by
> +                wait_for_inferior.  But since execution has stopped
> +                again, there is a new bpstat to look at, so start
> +                over.  */
> +             bpstat_do_actions (bsp);
> +
> +             /* Now we need to proceed with any actions of the old
> +                bpstat chain which are still not done.  But first, we
> +                need to restore the old chain, since it was blown
> +                away.  */
> +             *bsp = saved_bs;
> +             bs = *bsp;
> +             last_bpstat = *bsp;
> +
> +             /* Recursive invocation of bpstat_do_actions could reset
> +                breakpoint_proceeded and
> +                executing_breakpoint_commands, so restore their
> +                values.  */
> +             breakpoint_proceeded = 1;
> +             executing_breakpoint_commands = 1;
> +
> +             /* Skip all the actions that were already done.  */
> +             for (idx = 0; idx < bs_idx; idx++)
> +               {
> +                 bs->commands = NULL;
> +                 bs = bs->next;
> +               }
> +             cmd = bs->commands;
> +           }
> +         else
> +           {
> +             bpstat_clear (&saved_bs);
> +             discard_cleanups (pchain);
> +           }
> +         cmd_idx++;
> +         /* The command we just ran could have deleted the
> +            breakpoint.  This nukes the command lines for this
> +            breakpoint, so we cannot continue them.  */
> +         if (!breakpoint_alive_p (bs->breakpoint_at))
>             break;
> +         if (breakpoint_proceeded)
> +           {
> +             /* Skip the commands we already ran.  */
> +             for (idx = 0; idx < cmd_idx; idx++)
> +               cmd = cmd->next;
> +             breakpoint_proceeded = 0;
> +           }
>           else
>             cmd = cmd->next;
>         }
> -      if (breakpoint_proceeded)
> -       /* The inferior is proceeded by the command; bomb out now.
> -          The bpstat chain has been blown away by wait_for_inferior.
> -          But since execution has stopped again, there is a new bpstat
> -          to look at, so start over.  */
> -       goto top;
> -      else
> -       bs->commands = NULL;
> +      bs->commands = NULL;
>      }
> 
>    executing_breakpoint_commands = 0;

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