misc/ios: timeout and continue waiting for getwd
Split out from cl/8024 for clarity and improved approach.
Rarely, "stop reason = breakpoint" does not appear in the lldb stop
text. However the program is ready to proceed. To be a little more
robust about those cases, we wait for two seconds, and if that text
doesn't appear but a prompt does we continue and hope for the best.
Worst case, this results in a harder to read failure message.
Change-Id: Ib20aa92564cdccefd2b7260417c647cd44122b66
Reviewed-on: https://go-review.googlesource.com/8080
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/misc/ios/go_darwin_arm_exec.go b/misc/ios/go_darwin_arm_exec.go
index eb93005..f81e07e 100644
--- a/misc/ios/go_darwin_arm_exec.go
+++ b/misc/ios/go_darwin_arm_exec.go
@@ -150,8 +150,10 @@
// Manage the -test.timeout here, outside of the test. There is a lot
// of moving parts in an iOS test harness (notably lldb) that can
// swallow useful stdio or cause its own ruckus.
+ brTimeout := 5 * time.Second
var timedout chan struct{}
if t := parseTimeout(args); t > 1*time.Second {
+ brTimeout = t / 4
timedout = make(chan struct{})
time.AfterFunc(t-1*time.Second, func() {
close(timedout)
@@ -163,7 +165,7 @@
exited <- cmd.Wait()
}()
- waitFor := func(stage, str string) error {
+ waitFor := func(stage, str string, timeout time.Duration) error {
select {
case <-timedout:
w.printBuf()
@@ -174,20 +176,24 @@
case err := <-exited:
w.printBuf()
return fmt.Errorf("failed (stage %s): %v", stage, err)
- case i := <-w.find(str):
- w.clearTo(i + len(str))
+ case i := <-w.find(str, timeout):
+ if i >= 0 {
+ w.clearTo(i + len(str))
+ } else {
+ log.Printf("timed out on stage %s, continuing", stage)
+ }
return nil
}
}
do := func(cmd string) {
fmt.Fprintln(lldb, cmd)
- if err := waitFor(fmt.Sprintf("prompt after %q", cmd), "(lldb)"); err != nil {
+ if err := waitFor(fmt.Sprintf("prompt after %q", cmd), "(lldb)", 0); err != nil {
panic(waitPanic{err})
}
}
// Wait for installation and connection.
- if err := waitFor("ios-deploy before run", "(lldb) connect\r\nProcess 0 connected\r\n"); err != nil {
+ if err := waitFor("ios-deploy before run", "(lldb) connect\r\nProcess 0 connected\r\n", 0); err != nil {
return err
}
@@ -201,10 +207,12 @@
do(`breakpoint set -n getwd`) // in runtime/cgo/gcc_darwin_arm.go
fmt.Fprintln(lldb, `run`)
- if err := waitFor("br getwd", "stop reason = breakpoint"); err != nil {
+ // Sometimes we don't see "reason = breakpoint", so we time out
+ // and try to continue.
+ if err := waitFor("br getwd", "stop reason = breakpoint", brTimeout); err != nil {
return err
}
- if err := waitFor("br getwd prompt", "(lldb)"); err != nil {
+ if err := waitFor("br getwd prompt", "(lldb)", 0); err != nil {
return err
}
@@ -218,11 +226,11 @@
// Watch for SIGSEGV. Ideally lldb would never break on SIGSEGV.
// http://golang.org/issue/10043
go func() {
- <-w.find("stop reason = EXC_BAD_ACCESS")
+ <-w.find("stop reason = EXC_BAD_ACCESS", 0)
// cannot use do here, as the defer/recover is not available
// on this goroutine.
fmt.Fprintln(lldb, `bt`)
- waitFor("finish backtrace", "(lldb)")
+ waitFor("finish backtrace", "(lldb)", 0)
w.printBuf()
if p := cmd.Process; p != nil {
p.Kill()
@@ -261,8 +269,9 @@
buf []byte
suffix []byte // remove from each Write
- findTxt []byte // search buffer on each Write
- findCh chan int // report find position
+ findTxt []byte // search buffer on each Write
+ findCh chan int // report find position
+ findAfter *time.Timer
}
func (w *bufWriter) Write(in []byte) (n int, err error) {
@@ -280,6 +289,10 @@
close(w.findCh)
w.findTxt = nil
w.findCh = nil
+ if w.findAfter != nil {
+ w.findAfter.Stop()
+ w.findAfter = nil
+ }
}
}
return n, nil
@@ -307,7 +320,12 @@
w.buf = w.buf[i:]
}
-func (w *bufWriter) find(str string) <-chan int {
+// find returns a channel that will have exactly one byte index sent
+// to it when the text str appears in the buffer. If the text does not
+// appear before timeout, -1 is sent.
+//
+// A timeout of zero means no timeout.
+func (w *bufWriter) find(str string, timeout time.Duration) <-chan int {
w.mu.Lock()
defer w.mu.Unlock()
if len(w.findTxt) > 0 {
@@ -321,6 +339,19 @@
} else {
w.findTxt = txt
w.findCh = ch
+ if timeout > 0 {
+ w.findAfter = time.AfterFunc(timeout, func() {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ if w.findCh == ch {
+ w.findTxt = nil
+ w.findCh = nil
+ w.findAfter = nil
+ ch <- -1
+ close(ch)
+ }
+ })
+ }
}
return ch
}