Since this post I’ve tested this on more platforms (including cfarm machines with dejagnu-1.5.1 and tcl 8.5).
If there’s concern about applying it everywhere, I could make a second version of fixed_host_execute and have that called conditionally on Darwin. The Jit testsuite is unusable without this, and really I cannot enable Jit reliably on Darwin since one failure mode is a deadlocked expect instance so that the testsuite never completes until that is killed manually. thanks Iain > On 19 Aug 2021, at 19:59, Iain Sandoe <i...@sandoe.co.uk> wrote: > > Hi, > > Preface: > > this is the last patch for now in my series - with this applied Darwin > reports the same results as Linux (at least, for modern x86_64 > platform versions). > > Note > a) that the expect expression in {fixed}host_execute seems to depend > on the assumption that the dejagnu.h output is used by the testcase > and that the executable’s output can be seen to end with the totals > produced there (which might in itself be erroneous, see 3). > > b) the main GCC testsuite processing does not do this; rather the expect > expression is somewhat simple and the output from the executable > is copied into a secondary buffer, which is then processed by > prune expressions and then to find the requisite matches (so most > of the issues seen below do not occur there). > > ==== patch discussion > > The current 'fixed_host_execute' implementation fails on Darwin > platforms for a number of reasons: > > 1/ If the sub-process spawn fails (e.g. because of missing or mal- > formed params); rather than reporting the fail output into the > match stream, as indicated by the expect manual, it terminates > the script. > > - We fix this by (a) checking that the executable is valid as well > as existing (b) we put the spawn into a catch block and report > a failure. > > 2/ There is no recovery path at all for a buffer-full case (and we > do see buffer-full events with the default sizes). > > - Added by the patch here, however it is not as sophisticated as > the methods used by dejagnu internally. Here we set the process > to be "nowait" and then close the connection - with the intent > that this will terminate the spawned process. > > 3/ The expect logic assumes that 'Totals:' is a valid indicator > for the end of the spawned process output. This is not true > even for the default dejagnu header (there are a number of > additional reporting lines after). In addition to this, there > are some tests that intentionally produce more output after > the totals report (and there are tests that do not use that > mechanism at all). > > The effect is the we might arrive at the "wait" for the spawned > process to finish - but that process might not have completed > all its output. For Darwin, at least that causes a deadlock > between expect and the spawnee - the latter is doing a non- > cancellable write and the former is waiting for the latter to > terminate. For some reason this does not seem to affect Linux > perhaps the pty implementation allows the write(s) are able to > proceed even though there is no reader. > > - This is fixed by modifying the loop termination condition to be > either EOF (which will be the 'correct' condition) or a timeout > which would represent an error either in the runtime or in the > parsing of the output. As added precautions, we only try to > wait if there is a correcly-spawned process, and we are also > specific about which process we are waiting for. > > 4/ Darwin appears to have a bug in either the tcl or termios > 'cooking' code that ocassionally inserts an additional CR char > into the stream - thus '\n' => '\r\r\n' instead of '\r\n'. The > original program output is correct (it only contains a single > \n) - the additional character is being inserted somewhere in > the translations applied before the output reaches expect. > > The logic of this expect implementation does not tolerate single > \r or \n characters (it will fail with a timeout or buffer-full > if that occurs). > > - This is fixed by having a line-end match that is adjusted for > Darwin. > > 5/ The default buffer size does seem to be too small in some cases > noting that GCC uses 10000 as the match buffer size and the > default is 2000. > > - Fixed by increasing the size to 8192. > > 6/ There is a somewhat arbitrary dumping of output where we match > ^$prefix\tSOMETHING... and then process the something. This > essentially allows the match to start at any place in the buffer > following any collection of non-line-end chars. > > - Fixed by amending the match for 'general' lines to accommodate > these cases, and reporting such lines to the log. At least this > should allow debugging of any cases where output that should be > recognized is being dropped. > > tested on i686, x86_64-darwin, x86_64,powerpc64-linux, > OK for master? > thanks > Iain > > Signed-off-by: Iain Sandoe <i...@sandoe.co.uk> > > gcc/testsuite/ChangeLog: > > * jit.dg/jit.exp (fixed_local_execute): Amend the match and > exit conditions to cater for more platforms. > --- > gcc/testsuite/jit.dg/jit.exp | 123 +++++++++++++++++++++++------------ > 1 file changed, 83 insertions(+), 40 deletions(-) > > diff --git a/gcc/testsuite/jit.dg/jit.exp b/gcc/testsuite/jit.dg/jit.exp > index 005ba01601a..8541a44e1b2 100644 > --- a/gcc/testsuite/jit.dg/jit.exp > +++ b/gcc/testsuite/jit.dg/jit.exp > @@ -167,6 +167,9 @@ proc fixed_host_execute {args} { > if {![file exists ${executable}]} { > perror "The executable, \"$executable\" is missing" 0 > return "No source file found" > + } elseif {![file executable ${executable}]} { > + perror "The executable, \"$executable\" is not usable" 0 > + return "Bad executable found" > } > > verbose "params: $params" 2 > @@ -194,60 +197,100 @@ proc fixed_host_execute {args} { > set args [concat $args ${params}] > verbose "args: $args" 2 > > - eval spawn -noecho $args > - > - expect_after full_buffer { error "got full_buffer" } > + # We checked that the executable exists above, and can be executed, but > + # that does not cover other reasons that the launch could fail (e.g. > + # missing or malformed params); catch such cases here and report them. > + set err [catch "spawn -noecho $args" pid] > + set sub_proc_id $spawn_id > + if { $pid <= 0 || $err != 0 || $sub_proc_id < 0 } { > + warning "failed to spawn : $args : err = $err" > + } > + > + # Increase the buffer size, if needed to avoid spurious buffer-full > + # events; GCC uses 10000; chose a power of two here. > + set current_max_match [match_max -i $sub_proc_id] > + if { $current_max_match < 8192 } { > + match_max -i $sub_proc_id 8192 > + set used [match_max -i $sub_proc_id] > + } > + > + # If we get a buffer-full error, that seems to be unrecoverable so try to > + # exit in a reasonable manner to avoid wedged processes. > + expect_after full_buffer { > + verbose -log "fixed_host_execute: $args FULL BUFFER" > + # FIXME: this all assumes that closing the connection will cause the > + # sub-process to terminate (but that is not going to be the case if, > + # for example, there is something started with -nohup somewhere). > + # We should explicitly kill it here. > + # Set the process to be a nowait exit. > + wait -nowait -i $sub_proc_id > + catch close > + perror "${executable} got full_buffer" > + return "${executable} got full_buffer" > + } > > set prefix "\[^\r\n\]*" > + # Work around a Darwin tcl or termios bug that sometimes inserts extra > + # CR characters into the cooked tty stream > + set endline "\r\n" > + if { [istarget *-*-darwin*] } { > + set endline "\r(\r)*\n" > + } > + > + # Note that the logic here assumes that we cannot (validly) get single > + # carriage return or line feed characters in the stream. If those occur, > + # it will stop any further matching. We arange for the matching to be > + # at the start of the buffer - so that if there is any spurious output > + # to be discarded, it must be done explicitly - not by matching part-way > + # through the buffer. > expect { > - -re "^$prefix\[0-9\]\[0-9\]:..:..:${text}*\r\n" { > + -re "^$prefix\[0-9\]\[0-9\]:..:..:${text}*$endline" { > regsub "\[\n\r\t\]*NOTE: $text\r\n" $expect_out(0,string) "" output > verbose "$output" 3 > set timetol 0 > exp_continue > } > - -re "^$prefix\tNOTE:\[^\r\n\]+\r\n" { > - regsub "\[\n\r\t\]*NOTE: $text\r\n" $expect_out(0,string) "" output > - set output [string range $output 6 end-2] > - verbose "$output" 2 > + -re "^\tNOTE: (\[^\r\n\]+)$endline" { > + # discard notes. > + verbose "Ignored note: $expect_out(1,string)" 2 > set timetol 0 > exp_continue > } > - -re "^$prefix\tPASSED:\[^\r\n\]+\r\n" { > - regsub "\[\n\r\t\]*PASSED: $text\r\n" $expect_out(0,string) "" > output > - set output [string range $output 8 end-2] > - pass "$output" > + -re "^\tPASSED: (\[^\r\n\]+)$endline" { > + pass "$expect_out(1,string)" > set timetol 0 > exp_continue > } > - -re "^$prefix\tFAILED:\[^\r\n\]+\r\n" { > - regsub "\[\n\r\t\]*FAILED: $text\r\n" $expect_out(0,string) "" > output > - set output [string range $output 8 end-2] > - fail "$output" > + -re "^\tFAILED: (\[^\r\n\]+)$endline" { > + fail "$expect_out(1,string)" > set timetol 0 > exp_continue > } > - -re "^$prefix\tUNTESTED:\[^\r\n\]+\r\n" { > - regsub "\[\n\r\t\]*TESTED: $text\r\n" $expect_out(0,string) "" > output > - set output [string range $output 8 end-2] > - untested "$output" > + -re "^\tUNTESTED: (\[^\r\n\]+)$endline" { > + untested "$expect_out(1,string)" > set timetol 0 > exp_continue > } > - -re "^$prefix\tUNRESOLVED:\[^\r\n\]+\r\n" { > - regsub "\[\n\r\t\]*UNRESOLVED: $text\r\n" $expect_out(0,string) "" > output > - set output [string range $output 8 end-2] > - unresolved "$output" > + -re "^\tUNRESOLVED: (\[^\r\n\]+)$endline" { > + unresolved "$expect_out(1,string)" > set timetol 0 > exp_continue > } > - -re "^Totals" { > - verbose "All done" 2 > + -re "^$prefix$endline" { > + # This matches and discards any other lines (including blank > ones). > + if { [string length $expect_out(buffer)] <= 2 } { > + set output "blank line" > + } else { > + set output [string range $expect_out(buffer) 0 end-2] > + } > + verbose -log "DISCARDED $expect_out(spawn_id) : $output" > + exp_continue > } > eof { > - # unresolved "${executable} died prematurely" > - # catch close > - # return "${executable} died prematurely" > + # This seems to be the only way that we can reliably know that the > + # output is finished since there are cases where further output > + # follows the dejagnu test harness totals. > + verbose "saw eof" 2 > } > timeout { > warning "Timed out executing test case" > @@ -259,19 +302,19 @@ proc fixed_host_execute {args} { > return "Timed out executing test case" > } > } > - -re "^$prefix\r\n" { > - exp_continue > - } > } > > - # Use "wait" before "close": valgrind might not have finished > - # writing the log out before we parse it, so we need to wait for > - # the spawnee to finish. > - > - catch wait wres > - verbose "wres: $wres" 2 > - verify_exit_status $executable $wres > - > + # Use "wait" to pick up the sub-process exit state. If the sub-process > is > + # writing to a file (perhaps under valgrind) then that also needs to be > + # complete; only attempt this on a valid spawn. > + if { $sub_proc_id > 0 } { > + verbose "waiting for $sub_proc_id" 1 > + # Be explicit about what we are waiting for. > + catch "wait -i $sub_proc_id" wres > + verbose "wres: $wres" 2 > + verify_exit_status $executable $wres > + } > + > if $run_under_valgrind { > upvar 2 name name > parse_valgrind_logfile $name $valgrind_logfile > -- > 2.24.3 (Apple Git-128) >