Add a -wait-for-debugger flag to runner.
xterm on macOS is surprisingly difficult to get at these days. Instead,
add an option to make bssl_shim SIGSTOP itself so a debugger can resume
it.
Change-Id: Ie3cf02744557f46c8fa08c162276b5ff851a51c7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/41864
Commit-Queue: Steven Valdez <svaldez@google.com>
Reviewed-by: Steven Valdez <svaldez@google.com>
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index d8652ea..a91524a 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -1198,6 +1198,16 @@
return 0;
}
+ if (initial_config.wait_for_debugger) {
+#if defined(OPENSSL_WINDOWS)
+ fprintf(stderr, "-wait-for-debugger is not supported on Windows.\n");
+ return 1;
+#else
+ // The debugger will resume the process.
+ raise(SIGSTOP);
+#endif
+ }
+
bssl::UniquePtr<SSL_CTX> ssl_ctx;
bssl::UniquePtr<SSL_SESSION> session;
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index deddb03..e34980d 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -53,13 +53,14 @@
useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
useGDB = flag.Bool("gdb", false, "If true, run BoringSSL code under gdb")
useLLDB = flag.Bool("lldb", false, "If true, run BoringSSL code under lldb")
+ waitForDebugger = flag.Bool("wait-for-debugger", false, "If true, jobs will run one at a time and pause for a debugger to attach")
flagDebug = flag.Bool("debug", false, "Hexdump the contents of the connection")
mallocTest = flag.Int64("malloc-test", -1, "If non-negative, run each test with each malloc in turn failing from the given number onwards.")
mallocTestDebug = flag.Bool("malloc-test-debug", false, "If true, ask bssl_shim to abort rather than fail a malloc. This can be used with a specific value for --malloc-test to identity the malloc failing that is causing problems.")
jsonOutput = flag.String("json-output", "", "The file to output JSON results to.")
pipe = flag.Bool("pipe", false, "If true, print status output suitable for piping into another program.")
testToRun = flag.String("test", "", "The pattern to filter tests to run, or empty to run all tests")
- numWorkers = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.")
+ numWorkersFlag = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.")
shimPath = flag.String("shim-path", "../../../build/ssl/test/bssl_shim", "The location of the shim binary.")
handshakerPath = flag.String("handshaker-path", "../../../build/ssl/test/handshaker", "The location of the handshaker binary.")
resourceDir = flag.String("resource-dir", ".", "The directory in which to find certificate and key files.")
@@ -249,6 +250,10 @@
garbageCertificate.PrivateKey = rsaCertificate.PrivateKey
}
+func useDebugger() bool {
+ return *useGDB || *useLLDB || *waitForDebugger
+}
+
// delegatedCredentialConfig specifies the shape of a delegated credential, not
// including the keys themselves.
type delegatedCredentialConfig struct {
@@ -725,7 +730,9 @@
config.Time = func() time.Time { return time.Unix(1234, 1234) }
}
- conn = &timeoutConn{conn, *idleTimeout}
+ if !useDebugger() {
+ conn = &timeoutConn{conn, *idleTimeout}
+ }
if test.protocol == dtls {
config.Bugs.PacketAdaptor = newPacketAdaptor(conn)
@@ -1168,7 +1175,7 @@
}
connChan := make(chan connOrError, 1)
go func() {
- if !*useGDB {
+ if !useDebugger() {
listener.SetDeadline(time.Now().Add(*idleTimeout))
}
conn, err := listener.Accept()
@@ -1196,7 +1203,7 @@
return errorStr
}
-func runTest(test *testCase, shimPath string, mallocNumToFail int64) error {
+func runTest(statusChan chan statusMsg, test *testCase, shimPath string, mallocNumToFail int64) error {
// Help debugging panics on the Go side.
defer func() {
if r := recover(); r != nil {
@@ -1324,6 +1331,10 @@
flags = append(flags, "-handshaker-path", *handshakerPath)
+ if *waitForDebugger {
+ flags = append(flags, "-wait-for-debugger")
+ }
+
var transcriptPrefix string
var transcripts [][]byte
if len(*transcriptDir) != 0 {
@@ -1375,6 +1386,7 @@
if err := shim.Start(); err != nil {
panic(err)
}
+ statusChan <- statusMsg{test: test, statusType: statusShimStarted, pid: shim.Process.Pid}
waitChan := make(chan error, 1)
go func() { waitChan <- shim.Wait() }()
@@ -1417,7 +1429,7 @@
listener = nil
var childErr error
- if *useGDB {
+ if !useDebugger() {
childErr = <-waitChan
} else {
waitTimeout := time.AfterFunc(*idleTimeout, func() {
@@ -15548,8 +15560,8 @@
if *mallocTest >= 0 {
for mallocNumToFail := int64(*mallocTest); ; mallocNumToFail++ {
- statusChan <- statusMsg{test: test, started: true}
- if err = runTest(test, shimPath, mallocNumToFail); err != errMoreMallocs {
+ statusChan <- statusMsg{test: test, statusType: statusStarted}
+ if err = runTest(statusChan, test, shimPath, mallocNumToFail); err != errMoreMallocs {
if err != nil {
fmt.Printf("\n\nmalloc test failed at %d: %s\n", mallocNumToFail, err)
}
@@ -15558,21 +15570,30 @@
}
} else if *repeatUntilFailure {
for err == nil {
- statusChan <- statusMsg{test: test, started: true}
- err = runTest(test, shimPath, -1)
+ statusChan <- statusMsg{test: test, statusType: statusStarted}
+ err = runTest(statusChan, test, shimPath, -1)
}
} else {
- statusChan <- statusMsg{test: test, started: true}
- err = runTest(test, shimPath, -1)
+ statusChan <- statusMsg{test: test, statusType: statusStarted}
+ err = runTest(statusChan, test, shimPath, -1)
}
- statusChan <- statusMsg{test: test, err: err}
+ statusChan <- statusMsg{test: test, statusType: statusDone, err: err}
}
}
+type statusType int
+
+const (
+ statusStarted statusType = iota
+ statusShimStarted
+ statusDone
+)
+
type statusMsg struct {
- test *testCase
- started bool
- err error
+ test *testCase
+ statusType statusType
+ pid int
+ err error
}
func statusPrinter(doneChan chan *testresult.Results, statusChan chan statusMsg, total int) {
@@ -15589,9 +15610,9 @@
fmt.Print(erase)
}
- if msg.started {
+ if msg.statusType == statusStarted {
started++
- } else {
+ } else if msg.statusType == statusDone {
done++
if msg.err != nil {
@@ -15623,6 +15644,11 @@
if !*pipe {
// Print a new status line.
line := fmt.Sprintf("%d/%d/%d/%d/%d", failed, unimplemented, done, started, total)
+ if msg.statusType == statusShimStarted && *waitForDebugger {
+ // Note -wait-for-debugger limits the test to one worker,
+ // otherwise some output would be skipped.
+ line += fmt.Sprintf(" (%s: attach to process %d to continue)", msg.test.name, msg.pid)
+ }
lineLen = len(line)
os.Stdout.WriteString(line)
}
@@ -15683,8 +15709,13 @@
var wg sync.WaitGroup
- statusChan := make(chan statusMsg, *numWorkers)
- testChan := make(chan *testCase, *numWorkers)
+ numWorkers := *numWorkersFlag
+ if useDebugger() {
+ numWorkers = 1
+ }
+
+ statusChan := make(chan statusMsg, numWorkers)
+ testChan := make(chan *testCase, numWorkers)
doneChan := make(chan *testresult.Results)
if len(*shimConfigFile) != 0 {
@@ -15702,7 +15733,7 @@
go statusPrinter(doneChan, statusChan, len(testCases))
- for i := 0; i < *numWorkers; i++ {
+ for i := 0; i < numWorkers; i++ {
wg.Add(1)
go worker(statusChan, testChan, *shimPath, &wg)
}
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index d4b71b4..062a43a 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -152,6 +152,7 @@
&TestConfig::expect_delegated_credential_used},
{"-expect-hrr", &TestConfig::expect_hrr},
{"-expect-no-hrr", &TestConfig::expect_no_hrr},
+ {"-wait-for-debugger", &TestConfig::wait_for_debugger},
};
const Flag<std::string> kStringFlags[] = {
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 0974a16..f424f8b 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -177,6 +177,7 @@
std::string expect_early_data_reason;
bool expect_hrr = false;
bool expect_no_hrr = false;
+ bool wait_for_debugger = false;
int argc;
char **argv;