Add -rr-record flag to runner.go.

This flag causes the runner to execute the shim with the RR debugger.
See https://rr-project.org/.

Unlike typical debuggers, the RR workflow is to first record a session
and then replay it. The user cannot interact with the debugger while
recording and they replay the session multiple times. For these reasons,
I've opted not to launch xterm like -gdb and -lldb do.

The other difference is that -rr-record restricts the runner to exactly
one test. Otherwise, it's too easy to accumulate a bunch of unwanted
recordings. Also, `rr replay` uses the most recent recording by default,
so it's not very useful for runner to record multiple tests.

Change-Id: I2d29d64df5c4c832e50833325db3500ec2698e76
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/46144
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 9af820e..cc0fd06 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -54,6 +54,7 @@
 	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")
+	useRR                    = flag.Bool("rr-record", false, "If true, run BoringSSL code under `rr record`.")
 	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.")
@@ -253,7 +254,7 @@
 }
 
 func useDebugger() bool {
-	return *useGDB || *useLLDB || *waitForDebugger
+	return *useGDB || *useLLDB || *useRR || *waitForDebugger
 }
 
 // delegatedCredentialConfig specifies the shape of a delegated credential, not
@@ -1191,6 +1192,12 @@
 	return exec.Command("xterm", xtermArgs...)
 }
 
+func rrOf(path string, args ...string) *exec.Cmd {
+	rrArgs := []string{"record", path}
+	rrArgs = append(rrArgs, args...)
+	return exec.Command("rr", rrArgs...)
+}
+
 func removeFirstLineIfSuffix(s, suffix string) string {
 	idx := strings.IndexByte(s, '\n')
 	if idx < 0 {
@@ -1482,6 +1489,8 @@
 		shim = gdbOf(shimPath, flags...)
 	} else if *useLLDB {
 		shim = lldbOf(shimPath, flags...)
+	} else if *useRR {
+		shim = rrOf(shimPath, flags...)
 	} else {
 		shim = exec.Command(shimPath, flags...)
 	}
@@ -16860,6 +16869,11 @@
 		}
 
 		if matched {
+			if foundTest && *useRR {
+				fmt.Fprintf(os.Stderr, "Too many matching tests. Only one test can run when RR is enabled.\n")
+				os.Exit(1)
+			}
+
 			foundTest = true
 			testChan <- &testCases[i]
 
@@ -16888,6 +16902,10 @@
 		}
 	}
 
+	if *useRR {
+		fmt.Println("RR trace recorded. Replay with `rr replay`.")
+	}
+
 	if !testOutput.HasUnexpectedResults() {
 		os.Exit(1)
 	}