[PATCH 1/6] gdb/testsuite: Better detection of auto-response at y/n prompts

Simon Marchi simon.marchi@polymtl.ca
Wed Jan 9 14:45:00 GMT 2019


On 2019-01-09 06:24, Andrew Burgess wrote:
> * Simon Marchi <simon.marchi@polymtl.ca> [2019-01-09 00:03:49 -0500]:
> 
>> On 2019-01-07 04:00, Andrew Burgess wrote:
>> > * Simon Marchi <simon.marchi@ericsson.com> [2019-01-02 21:48:06 +0000]:
>> >
>> > > On 2019-01-01 5:45 p.m., Andrew Burgess wrote:
>> > > > I noticed that when running this test:
>> > > >
>> > > >   make check-gdb RUNTESTFLAGS="--target_board=native-gdbserver gdb.mi/mi-break.exp"
>> > > >
>> > > > I would occasionally see some UNRESOLVED test results like this:
>> > > >
>> > > >   (gdb)
>> > > >   PASS: gdb.mi/mi-break.exp: mi-mode=separate: breakpoint at main
>> > > >   Expecting: ^(kill[
>> > > >   ]+)?(.*[
>> > > >   ]+[(]gdb[)]
>> > > >   [ ]*)
>> > > >   kill
>> > > >   &"kill\n"
>> > > >   ~"Kill the program being debugged? (y or n) [answered Y; input not from terminal]\n"
>> > > >   =thread-group-exited,id="i1"
>> > > >   ERROR: Got interactive prompt.
>> > > >   UNRESOLVED: gdb.mi/mi-break.exp: mi-mode=separate:
>> > > >
>> > > > The problem appears to be that the expect buffer fills up to include
>> > > > the '(y or n)' prompt without including the following lines.
>> > > >
>> > > > The pattern supplied by the outer test script is looking for the
>> > > > following lines.  As the following lines are not present then expect
>> > > > matches on the interactive prompt case rather than the case for the
>> > > > user supplied pattern.
>> > > >
>> > > > The problem with this is that we are not really at an interactive
>> > > > prompt, GDB is providing an answer for us and then moving on.  When I
>> > > > examine a successful run of the test the output from GDB is identical,
>> > > > the only difference is where expect happens to buffer the output from
>> > > > GDB.
>> > > >
>> > > > This patch introduces a second check inside the 'y or n' prompt case,
>> > > > which gives expect a chance to refill its buffers and catches the
>> > > > 'answered Y; input ...' text.
>> > > >
>> > > > This second check is on a short 1 second timeout, I'm currently
>> > > > assuming that the auto-answer text will either already be in expect,
>> > > > or is waiting to be read in.  If after 1 second the auto-answer text
>> > > > is not seen then we assume that GDB really is waiting at an
>> > > > interactive prompt.
>> > > >
>> > > > With this patch in place I can now leave the following loop running
>> > > > indefinitely, where before it would fail usually after ~10
>> > > > iterations.
>> > >
>> > > For me it fails consistently the way you describe.
>> > >
>> > > > diff --git a/gdb/testsuite/lib/mi-support.exp b/gdb/testsuite/lib/mi-support.exp
>> > > > index d193592a843..48ea45d62c7 100644
>> > > > --- a/gdb/testsuite/lib/mi-support.exp
>> > > > +++ b/gdb/testsuite/lib/mi-support.exp
>> > > > @@ -834,9 +834,24 @@ proc mi_gdb_test { args } {
>> > > >  	     fail "$message"
>> > > >  	}
>> > > >  	 -re "\\(y or n\\) " {
>> > > > -	    send_gdb "n\n"
>> > > > -	    perror "Got interactive prompt."
>> > > > -	     fail "$message"
>> > > > +	    # If the expect buffer just happens to fill up to the 'y
>> > > > +	    # or n' prompt then we can end up in this case, even
>> > > > +	    # though GDB will automatically provide a response for us.
>> > > > +	    # We give expect another chance here to look for the auto
>> > > > +	    # answer text before declaring a fail.
>> > > > +	    set auto_response_seen 0
>> > > > +	    gdb_expect 1 {
>> > > > +		-re ".answered Y; input not from terminal." {
>> > > > +		    set auto_response_seen 1
>> > > > +		}
>> > > > +	    }
>> > > > +	    if { ! $auto_response_seen } {
>> > > > +		send_gdb "n\n"
>> > > > +		perror "Got interactive prompt."
>> > > > +		fail "$message"
>> > > > +	    } else {
>> > > > +		exp_continue
>> > > > +	    }
>> > > >  	}
>> > > >  	 eof {
>> > > >  	     perror "Process no longer exists"
>> > >
>> > > Do we need this stanza at all?  I understand that it can save some
>> > > time (avoid having
>> > > to wait for the timeout) if the MI interpreter suddenly starts
>> > > asking interactive
>> > > "y or n" questions (which it should not do), but since it's causing
>> > > this kind of trouble,
>> > > maybe we can just get rid of it?
>> >
>> > I don't see any real reason to keep it.  I tried deleting it and no
>> > tests seem to regress, and the previously unstable test is now stable.
>> >
>> > Is this OK to apply?
>> >
>> > Thanks,
>> > Andrew
>> >
>> > --
>> >
>> > gdb/testsuite: Remove interactive prompt case from mi_gdb_test
>> >
>> > I noticed that when running this test:
>> >
>> >   make check-gdb RUNTESTFLAGS="--target_board=native-gdbserver
>> > gdb.mi/mi-break.exp"
>> >
>> > I would occasionally see some UNRESOLVED test results like this:
>> >
>> >   (gdb)
>> >   PASS: gdb.mi/mi-break.exp: mi-mode=separate: breakpoint at main
>> >   Expecting: ^(kill[
>> >   ]+)?(.*[
>> >   ]+[(]gdb[)]
>> >   [ ]*)
>> >   kill
>> >   &"kill\n"
>> >   ~"Kill the program being debugged? (y or n) [answered Y; input not
>> > from terminal]\n"
>> >   =thread-group-exited,id="i1"
>> >   ERROR: Got interactive prompt.
>> >   UNRESOLVED: gdb.mi/mi-break.exp: mi-mode=separate:
>> >
>> > The problem appears to be that the expect buffer fills up to include
>> > the '(y or n)' prompt without including the following lines.
>> >
>> > The pattern supplied by the outer test script is looking for the
>> > following lines.  As the following lines are not present then expect
>> > matches on the interactive prompt case rather than the case for the
>> > user supplied pattern.
>> >
>> > The problem with this is that we are not really at an interactive
>> > prompt, GDB is providing an answer for us and then moving on.  When I
>> > examine a successful run of the test the output from GDB is identical,
>> > the only difference is where expect happens to buffer the output from
>> > GDB.
>> >
>> > This patch remove all special handling of the interactive prompt
>> > case.  This means that if we ever break GDB and start seeing an
>> > unexpected interactive prompt then tests will rely on a timeout to
>> > fail, instead of having dedicated interactive prompt detection, but
>> > this solves the problem that an auto-answered prompt looks very
>> > similar to an interactive prompt.
>> >
>> > With this patch in place I can now leave the following loop running
>> > indefinitely, where before it would fail usually after ~10
>> > iterations.
>> >
>> >   while make check-gdb RUNTESTFLAGS="--target_board=native-gdbserver
>> > gdb.mi/mi-break.exp"; \
>> >   do /bin/true; \
>> >   done
>> >
>> > gdb/testsuite/ChangeLog:
>> >
>> > 	* lib/mi-support.exp (mi_gdb_test): Remove interactive prompt
>> > 	case.
>> > ---
>> >  gdb/testsuite/ChangeLog          | 5 +++++
>> >  gdb/testsuite/lib/mi-support.exp | 5 -----
>> >  2 files changed, 5 insertions(+), 5 deletions(-)
>> >
>> > diff --git a/gdb/testsuite/lib/mi-support.exp
>> > b/gdb/testsuite/lib/mi-support.exp
>> > index d193592a843..a58c4f6e119 100644
>> > --- a/gdb/testsuite/lib/mi-support.exp
>> > +++ b/gdb/testsuite/lib/mi-support.exp
>> > @@ -832,11 +832,6 @@ proc mi_gdb_test { args } {
>> >  	    send_gdb "\n"
>> >  	    perror "Window too small."
>> >  	     fail "$message"
>> > -	}
>> > -	 -re "\\(y or n\\) " {
>> > -	    send_gdb "n\n"
>> > -	    perror "Got interactive prompt."
>> > -	     fail "$message"
>> >  	}
>> >  	 eof {
>> >  	     perror "Process no longer exists"
>> 
>> 
>> Thanks, this LGTM.
> 
> Thanks, I pushed this patch.
>> 
>> Btw, what you think about those "fail"s following "perror"s?  It's my
>> understanding that perror throws an error, interrupting the execution 
>> flow,
>> so the fail is not recorded, right?
> 
> That's not what I see.  As an experiment I created the file
> gdb.base/perror-test.exp like this:
> 
>   perror "this is an error"
>   pass "this is a pass"
> 
> With this I see:
> 
>   ERROR: this is an error
> 
>   		=== gdb Summary ===
> 
>   # of unresolved testcases	1
> 
> With the unresolved being instead of the 'PASS'.  Also the return
> value from runtest is non-zero.
> 
> If I change perror-test.exp to this:
> 
>   perror "this is an error"
>   fail "this is a fail"
> 
> Then the results are similar:
> 
>   ERROR: this is an error
> 
>   		=== gdb Summary ===
> 
>   # of unresolved testcases	1
> 
> Again, the unresolved replaces the 'FAIL', and the exit value is
> non-zero.
> 
> Where it gets interesting is if I change perror-test.exp to this:
> 
>   perror "this is an error"
> 
> Then I get this result:
> 
>   ERROR: this is an error
> 
>   		=== gdb Summary ===
> 
> And alarmingly (maybe?) the exit value from runtest is 0, so it looks
> like the test has passed.
> 
> My conclusions then:
> 
>   - perror doesn't interrupt control flow,
>   - perror doesn't itself cause the test script to appear to fail,
>   - perror causes the next pass/fail (at least) to become unresolved,
> 
> Given that the perror calls we're looking at here are in mi_gdb_test,
> the entire pass/fail printing is wrapped up within this method, I
> don't think the unresolved-ness should leak out into the next test,
> so, in this case I think perror/fail is correct.

Ah, this is clearer when reading the documentation :)

https://www.gnu.org/software/dejagnu/manual/perror-procedure.html

It's a bit confusing, since it overrides the following pass/fail 
message.  If you don't know what perror does, it looks like the 
pass/fail is not reached...  Thanks for looking into it.

>> And a note, maybe you are already aware of its existence, but I think 
>> that
>> "make check-read1" could have been useful to reproduce the issue 
>> reliably
>> here.  It forces the read syscall to read 1 byte at the time, so you 
>> are
>> certain to reach a point where -re "\\(y or n\\) " matches.
> 
> I did not know this.  Might be worth testing the whole testsuite
> against this and look for regressions..... I'll add it to my (long)
> list.

At least, it can be a good practice to check the tests we are working 
with it.

Simon



More information about the Gdb-patches mailing list