var update … var verbose … var dryrun … var useGdb … var force … var repeats … var inlines … var hexRe … var numRe … var stringRe … var leadingDollarNumberRe … var optOutGdbRe … var numberColonRe … var gdb … var debugger … var gogcflags … var optimizedLibs … // TestNexting go-builds a file, then uses a debugger (default delve, optionally gdb) // to next through the generated executable, recording each line landed at, and // then compares those lines with reference file(s). // Flag -u updates the reference file(s). // Flag -g changes the debugger to gdb (and uses gdb-specific reference files) // Flag -v is ever-so-slightly verbose. // Flag -n is for dry-run, and prints the shell and first debug commands. // // Because this test (combined with existing compiler deficiencies) is flaky, // for gdb-based testing by default inlining is disabled // (otherwise output depends on library internals) // and for both gdb and dlv by default repeated lines in the next stream are ignored // (because this appears to be timing-dependent in gdb, and the cleanest fix is in code common to gdb and dlv). // // Also by default, any source code outside of .../testdata/ is not mentioned // in the debugging histories. This deals both with inlined library code once // the compiler is generating clean inline records, and also deals with // runtime code between return from main and process exit. This is hidden // so that those files (in the runtime/library) can change without affecting // this test. // // These choices can be reversed with -i (inlining on) and -r (repeats detected) which // will also cause their own failures against the expected outputs. Note that if the compiler // and debugger were behaving properly, the inlined code and repeated lines would not appear, // so the expected output is closer to what we hope to see, though it also encodes all our // current bugs. // // The file being tested may contain comments of the form // //DBG-TAG=(v1,v2,v3) // where DBG = {gdb,dlv} and TAG={dbg,opt} // each variable may optionally be followed by a / and one or more of S,A,N,O // to indicate normalization of Strings, (hex) addresses, and numbers. // "O" is an explicit indication that we expect it to be optimized out. // For example: // // if len(os.Args) > 1 { //gdb-dbg=(hist/A,cannedInput/A) //dlv-dbg=(hist/A,cannedInput/A) // // TODO: not implemented for Delve yet, but this is the plan // // After a compiler change that causes a difference in the debug behavior, check // to see if it is sensible or not, and if it is, update the reference files with // go test debug_test.go -args -u // (for Delve) // go test debug_test.go -args -u -d func TestNexting(t *testing.T) { … } // subTest creates a subtest that compiles basename.go with the specified gcflags and additional compiler arguments, // then runs the debugger on the resulting binary, with any comment-specified actions matching tag triggered. func subTest(t *testing.T, tag string, basename string, gcflags string, moreargs ...string) { … } // skipSubTest is the same as subTest except that it skips the test if execution is not forced (-f) func skipSubTest(t *testing.T, tag string, basename string, gcflags string, count int, moreargs ...string) { … } // optSubTest is the same as subTest except that it skips the test if the runtime and libraries // were not compiled with optimization turned on. (The skip may not be necessary with Go 1.10 and later) func optSubTest(t *testing.T, tag string, basename string, gcflags string, count int, moreargs ...string) { … } func testNexting(t *testing.T, base, tag, gcflags string, count int, moreArgs ...string) { … } type dbgr … func runDbgr(dbg dbgr, maxNext int) *nextHist { … } func runGo(t *testing.T, dir string, args ...string) string { … } type tstring … func (t tstring) String() string { … } type pos … type nextHist … func (h *nextHist) write(filename string) { … } func (h *nextHist) read(filename string) { … } // add appends file (name), line (number) and text (string) to the history, // provided that the file+line combo does not repeat the previous position, // and provided that the file is within the testdata directory. The return // value indicates whether the append occurred. func (h *nextHist) add(file, line, text string) bool { … } func (h *nextHist) addVar(text string) { … } func invertMapSU8(hf2i map[string]uint8) map[uint8]string { … } func (h *nextHist) equals(k *nextHist) bool { … } // canonFileName strips everything before "/src/" from a filename. // This makes file names portable across different machines, // home directories, and temporary directories. func canonFileName(f string) string { … } type delveState … func newDelve(t testing.TB, tag, executable string, args ...string) dbgr { … } func (s *delveState) tag() string { … } func (s *delveState) stepnext(ss string) bool { … } func (s *delveState) start() { … } func (s *delveState) quit() { … } type gdbState … func newGdb(t testing.TB, tag, executable string, args ...string) dbgr { … } func (s *gdbState) tag() string { … } func (s *gdbState) start() { … } func (s *gdbState) stepnext(ss string) bool { … } // printVariableAndNormalize extracts any slash-indicated normalizing requests from the variable // name, then uses printer to get the value of the variable from the debugger, and then // normalizes and returns the response. func printVariableAndNormalize(v string, printer func(v string) string) string { … } // varsToPrint takes a source code line, and extracts the comma-separated variable names // found between lookfor and the next ")". // For example, if line includes "... //gdb-foo=(v1,v2,v3)" and // lookfor="//gdb-foo=(", then varsToPrint returns ["v1", "v2", "v3"] func varsToPrint(line, lookfor string) []string { … } func (s *gdbState) quit() { … } type ioState … func newIoState(cmd *exec.Cmd) *ioState { … } func (s *ioState) hist() *nextHist { … } // writeRead writes ss, then reads stdout and stderr, waiting 500ms to // be sure all the output has appeared. func (s *ioState) writeRead(ss string) tstring { … } // writeReadExpect writes ss, then reads stdout and stderr until something // that matches expectRE appears. expectRE should not be "" func (s *ioState) writeReadExpect(ss, expectRE string) tstring { … } func (s *ioState) readExpecting(millis, interlineTimeout int, expectedRE string) tstring { … } func (s *ioState) readSimpleExpecting(expectedRE string) tstring { … } // replaceEnv returns a new environment derived from env // by removing any existing definition of ev and adding ev=evv. func replaceEnv(env []string, ev string, evv string) []string { … } // asCommandLine renders cmd as something that could be copy-and-pasted into a command line // If cwd is not empty and different from the command's directory, prepend an appropriate "cd" func asCommandLine(cwd string, cmd *exec.Cmd) string { … } // escape inserts escapes appropriate for use in a shell command line func escape(s string) string { … } func expect(want string, got tstring) { … }