Source file src/runtime/crash_test.go

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package runtime_test
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"errors"
    11  	"flag"
    12  	"fmt"
    13  	"internal/asan"
    14  	"internal/msan"
    15  	"internal/profile"
    16  	"internal/race"
    17  	"internal/testenv"
    18  	traceparse "internal/trace"
    19  	"io"
    20  	"log"
    21  	"os"
    22  	"os/exec"
    23  	"path/filepath"
    24  	"regexp"
    25  	"runtime"
    26  	"runtime/trace"
    27  	"strings"
    28  	"sync"
    29  	"testing"
    30  	"time"
    31  )
    32  
    33  var toRemove []string
    34  
    35  const entrypointVar = "RUNTIME_TEST_ENTRYPOINT"
    36  
    37  func TestMain(m *testing.M) {
    38  	switch entrypoint := os.Getenv(entrypointVar); entrypoint {
    39  	case "panic":
    40  		crashViaPanic()
    41  		panic("unreachable")
    42  	case "trap":
    43  		crashViaTrap()
    44  		panic("unreachable")
    45  	default:
    46  		log.Fatalf("invalid %s: %q", entrypointVar, entrypoint)
    47  	case "":
    48  		// fall through to normal behavior
    49  	}
    50  
    51  	_, coreErrBefore := os.Stat("core")
    52  
    53  	status := m.Run()
    54  	for _, file := range toRemove {
    55  		os.RemoveAll(file)
    56  	}
    57  
    58  	_, coreErrAfter := os.Stat("core")
    59  	if coreErrBefore != nil && coreErrAfter == nil {
    60  		fmt.Fprintln(os.Stderr, "runtime.test: some test left a core file behind")
    61  		if status == 0 {
    62  			status = 1
    63  		}
    64  	}
    65  
    66  	os.Exit(status)
    67  }
    68  
    69  var testprog struct {
    70  	sync.Mutex
    71  	dir    string
    72  	target map[string]*buildexe
    73  }
    74  
    75  type buildexe struct {
    76  	once sync.Once
    77  	exe  string
    78  	err  error
    79  }
    80  
    81  func runTestProg(t *testing.T, binary, name string, env ...string) string {
    82  	if *flagQuick {
    83  		t.Skip("-quick")
    84  	}
    85  
    86  	testenv.MustHaveGoBuild(t)
    87  	t.Helper()
    88  
    89  	exe, err := buildTestProg(t, binary)
    90  	if err != nil {
    91  		t.Fatal(err)
    92  	}
    93  
    94  	return runBuiltTestProg(t, exe, name, env...)
    95  }
    96  
    97  func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
    98  	t.Helper()
    99  
   100  	if *flagQuick {
   101  		t.Skip("-quick")
   102  	}
   103  
   104  	start := time.Now()
   105  
   106  	cmd := testenv.CleanCmdEnv(testenv.Command(t, exe, name))
   107  	cmd.Env = append(cmd.Env, env...)
   108  	if testing.Short() {
   109  		cmd.Env = append(cmd.Env, "RUNTIME_TEST_SHORT=1")
   110  	}
   111  	out, err := cmd.CombinedOutput()
   112  	if err == nil {
   113  		t.Logf("%v (%v): ok", cmd, time.Since(start))
   114  	} else {
   115  		if _, ok := err.(*exec.ExitError); ok {
   116  			t.Logf("%v: %v", cmd, err)
   117  		} else if errors.Is(err, exec.ErrWaitDelay) {
   118  			t.Fatalf("%v: %v", cmd, err)
   119  		} else {
   120  			t.Fatalf("%v failed to start: %v", cmd, err)
   121  		}
   122  	}
   123  	return string(out)
   124  }
   125  
   126  var serializeBuild = make(chan bool, 2)
   127  
   128  func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) {
   129  	if *flagQuick {
   130  		t.Skip("-quick")
   131  	}
   132  	testenv.MustHaveGoBuild(t)
   133  
   134  	testprog.Lock()
   135  	if testprog.dir == "" {
   136  		dir, err := os.MkdirTemp("", "go-build")
   137  		if err != nil {
   138  			t.Fatalf("failed to create temp directory: %v", err)
   139  		}
   140  		testprog.dir = dir
   141  		toRemove = append(toRemove, dir)
   142  	}
   143  
   144  	if testprog.target == nil {
   145  		testprog.target = make(map[string]*buildexe)
   146  	}
   147  	name := binary
   148  	if len(flags) > 0 {
   149  		name += "_" + strings.Join(flags, "_")
   150  	}
   151  	target, ok := testprog.target[name]
   152  	if !ok {
   153  		target = &buildexe{}
   154  		testprog.target[name] = target
   155  	}
   156  
   157  	dir := testprog.dir
   158  
   159  	// Unlock testprog while actually building, so that other
   160  	// tests can look up executables that were already built.
   161  	testprog.Unlock()
   162  
   163  	target.once.Do(func() {
   164  		// Only do two "go build"'s at a time,
   165  		// to keep load from getting too high.
   166  		serializeBuild <- true
   167  		defer func() { <-serializeBuild }()
   168  
   169  		// Don't get confused if testenv.GoToolPath calls t.Skip.
   170  		target.err = errors.New("building test called t.Skip")
   171  
   172  		if asan.Enabled {
   173  			flags = append(flags, "-asan")
   174  		}
   175  		if msan.Enabled {
   176  			flags = append(flags, "-msan")
   177  		}
   178  		if race.Enabled {
   179  			flags = append(flags, "-race")
   180  		}
   181  
   182  		exe := filepath.Join(dir, name+".exe")
   183  
   184  		start := time.Now()
   185  		cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...)
   186  		t.Logf("running %v", cmd)
   187  		cmd.Dir = "testdata/" + binary
   188  		cmd = testenv.CleanCmdEnv(cmd)
   189  
   190  		// If tests need any experimental flags, add them here.
   191  		//
   192  		// TODO(vsaioc): Remove `goroutineleakprofile` once the feature is no longer experimental.
   193  		edited := false
   194  		for i := range cmd.Env {
   195  			e := cmd.Env[i]
   196  			if _, vars, ok := strings.Cut(e, "GOEXPERIMENT="); ok {
   197  				cmd.Env[i] = "GOEXPERIMENT=" + vars + ",goroutineleakprofile"
   198  				edited, _ = true, vars
   199  			}
   200  		}
   201  		if !edited {
   202  			cmd.Env = append(cmd.Env, "GOEXPERIMENT=goroutineleakprofile")
   203  		}
   204  
   205  		out, err := cmd.CombinedOutput()
   206  		if err != nil {
   207  			target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out)
   208  		} else {
   209  			t.Logf("built %v in %v", name, time.Since(start))
   210  			target.exe = exe
   211  			target.err = nil
   212  		}
   213  	})
   214  
   215  	return target.exe, target.err
   216  }
   217  
   218  func TestVDSO(t *testing.T) {
   219  	t.Parallel()
   220  	output := runTestProg(t, "testprog", "SignalInVDSO")
   221  	want := "success\n"
   222  	if output != want {
   223  		t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
   224  	}
   225  }
   226  
   227  func testCrashHandler(t *testing.T, cgo bool) {
   228  	type crashTest struct {
   229  		Cgo bool
   230  	}
   231  	var output string
   232  	if cgo {
   233  		if runtime.GOOS == "freebsd" && race.Enabled {
   234  			t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
   235  		}
   236  		output = runTestProg(t, "testprogcgo", "Crash")
   237  	} else {
   238  		output = runTestProg(t, "testprog", "Crash")
   239  	}
   240  	want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
   241  	if output != want {
   242  		t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
   243  	}
   244  }
   245  
   246  func TestCrashHandler(t *testing.T) {
   247  	testCrashHandler(t, false)
   248  }
   249  
   250  var deadlockBuildTypes = testenv.SpecialBuildTypes{
   251  	// External linking brings in cgo, causing deadlock detection not working.
   252  	Cgo:  false,
   253  	Asan: asan.Enabled,
   254  	Msan: msan.Enabled,
   255  	Race: race.Enabled,
   256  }
   257  
   258  func testDeadlock(t *testing.T, name string) {
   259  	// External linking brings in cgo, causing deadlock detection not working.
   260  	testenv.MustInternalLink(t, deadlockBuildTypes)
   261  
   262  	output := runTestProg(t, "testprog", name)
   263  	want := "fatal error: all goroutines are asleep - deadlock!\n"
   264  	if !strings.HasPrefix(output, want) {
   265  		t.Fatalf("output does not start with %q:\n%s", want, output)
   266  	}
   267  }
   268  
   269  func TestSimpleDeadlock(t *testing.T) {
   270  	testDeadlock(t, "SimpleDeadlock")
   271  }
   272  
   273  func TestInitDeadlock(t *testing.T) {
   274  	testDeadlock(t, "InitDeadlock")
   275  }
   276  
   277  func TestLockedDeadlock(t *testing.T) {
   278  	testDeadlock(t, "LockedDeadlock")
   279  }
   280  
   281  func TestLockedDeadlock2(t *testing.T) {
   282  	testDeadlock(t, "LockedDeadlock2")
   283  }
   284  
   285  func TestGoexitDeadlock(t *testing.T) {
   286  	// External linking brings in cgo, causing deadlock detection not working.
   287  	testenv.MustInternalLink(t, deadlockBuildTypes)
   288  
   289  	output := runTestProg(t, "testprog", "GoexitDeadlock")
   290  	want := "no goroutines (main called runtime.Goexit) - deadlock!"
   291  	if !strings.Contains(output, want) {
   292  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   293  	}
   294  }
   295  
   296  func TestStackOverflow(t *testing.T) {
   297  	output := runTestProg(t, "testprog", "StackOverflow")
   298  	want := []string{
   299  		"runtime: goroutine stack exceeds 1474560-byte limit\n",
   300  		"fatal error: stack overflow",
   301  		// information about the current SP and stack bounds
   302  		"runtime: sp=",
   303  		"stack=[",
   304  	}
   305  	if !strings.HasPrefix(output, want[0]) {
   306  		t.Errorf("output does not start with %q", want[0])
   307  	}
   308  	for _, s := range want[1:] {
   309  		if !strings.Contains(output, s) {
   310  			t.Errorf("output does not contain %q", s)
   311  		}
   312  	}
   313  	if t.Failed() {
   314  		t.Logf("output:\n%s", output)
   315  	}
   316  }
   317  
   318  func TestThreadExhaustion(t *testing.T) {
   319  	output := runTestProg(t, "testprog", "ThreadExhaustion")
   320  	want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
   321  	if !strings.HasPrefix(output, want) {
   322  		t.Fatalf("output does not start with %q:\n%s", want, output)
   323  	}
   324  }
   325  
   326  func TestRecursivePanic(t *testing.T) {
   327  	output := runTestProg(t, "testprog", "RecursivePanic")
   328  	want := `wrap: bad
   329  panic: again
   330  
   331  `
   332  	if !strings.HasPrefix(output, want) {
   333  		t.Fatalf("output does not start with %q:\n%s", want, output)
   334  	}
   335  
   336  }
   337  
   338  func TestRecursivePanic2(t *testing.T) {
   339  	output := runTestProg(t, "testprog", "RecursivePanic2")
   340  	want := `first panic
   341  second panic
   342  panic: third panic
   343  
   344  `
   345  	if !strings.HasPrefix(output, want) {
   346  		t.Fatalf("output does not start with %q:\n%s", want, output)
   347  	}
   348  
   349  }
   350  
   351  func TestRecursivePanic3(t *testing.T) {
   352  	output := runTestProg(t, "testprog", "RecursivePanic3")
   353  	want := `panic: first panic
   354  
   355  `
   356  	if !strings.HasPrefix(output, want) {
   357  		t.Fatalf("output does not start with %q:\n%s", want, output)
   358  	}
   359  
   360  }
   361  
   362  func TestRecursivePanic4(t *testing.T) {
   363  	output := runTestProg(t, "testprog", "RecursivePanic4")
   364  	want := `panic: first panic [recovered]
   365  	panic: second panic
   366  `
   367  	if !strings.HasPrefix(output, want) {
   368  		t.Fatalf("output does not start with %q:\n%s", want, output)
   369  	}
   370  
   371  }
   372  
   373  func TestRecursivePanic5(t *testing.T) {
   374  	output := runTestProg(t, "testprog", "RecursivePanic5")
   375  	want := `first panic
   376  second panic
   377  panic: third panic
   378  `
   379  	if !strings.HasPrefix(output, want) {
   380  		t.Fatalf("output does not start with %q:\n%s", want, output)
   381  	}
   382  
   383  }
   384  
   385  func TestRepanickedPanic(t *testing.T) {
   386  	output := runTestProg(t, "testprog", "RepanickedPanic")
   387  	want := `panic: message [recovered, repanicked]
   388  `
   389  	if !strings.HasPrefix(output, want) {
   390  		t.Fatalf("output does not start with %q:\n%s", want, output)
   391  	}
   392  }
   393  
   394  func TestRepanickedMiddlePanic(t *testing.T) {
   395  	output := runTestProg(t, "testprog", "RepanickedMiddlePanic")
   396  	want := `panic: inner [recovered]
   397  	panic: middle [recovered, repanicked]
   398  	panic: outer
   399  `
   400  	if !strings.HasPrefix(output, want) {
   401  		t.Fatalf("output does not start with %q:\n%s", want, output)
   402  	}
   403  }
   404  
   405  func TestRepanickedPanicSandwich(t *testing.T) {
   406  	output := runTestProg(t, "testprog", "RepanickedPanicSandwich")
   407  	want := `panic: outer [recovered]
   408  	panic: inner [recovered]
   409  	panic: outer
   410  `
   411  	if !strings.HasPrefix(output, want) {
   412  		t.Fatalf("output does not start with %q:\n%s", want, output)
   413  	}
   414  }
   415  
   416  func TestDoublePanicWithSameValue(t *testing.T) {
   417  	output := runTestProg(t, "testprog", "DoublePanicWithSameValue")
   418  	want := `panic: message
   419  `
   420  	if !strings.HasPrefix(output, want) {
   421  		t.Fatalf("output does not start with %q:\n%s", want, output)
   422  	}
   423  }
   424  
   425  func TestGoexitCrash(t *testing.T) {
   426  	// External linking brings in cgo, causing deadlock detection not working.
   427  	testenv.MustInternalLink(t, deadlockBuildTypes)
   428  
   429  	output := runTestProg(t, "testprog", "GoexitExit")
   430  	want := "no goroutines (main called runtime.Goexit) - deadlock!"
   431  	if !strings.Contains(output, want) {
   432  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   433  	}
   434  }
   435  
   436  func TestGoexitDefer(t *testing.T) {
   437  	c := make(chan struct{})
   438  	go func() {
   439  		defer func() {
   440  			r := recover()
   441  			if r != nil {
   442  				t.Errorf("non-nil recover during Goexit")
   443  			}
   444  			c <- struct{}{}
   445  		}()
   446  		runtime.Goexit()
   447  	}()
   448  	// Note: if the defer fails to run, we will get a deadlock here
   449  	<-c
   450  }
   451  
   452  func TestGoNil(t *testing.T) {
   453  	output := runTestProg(t, "testprog", "GoNil")
   454  	want := "go of nil func value"
   455  	if !strings.Contains(output, want) {
   456  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   457  	}
   458  }
   459  
   460  func TestMainGoroutineID(t *testing.T) {
   461  	output := runTestProg(t, "testprog", "MainGoroutineID")
   462  	want := "panic: test\n\ngoroutine 1 [running]:\n"
   463  	if !strings.HasPrefix(output, want) {
   464  		t.Fatalf("output does not start with %q:\n%s", want, output)
   465  	}
   466  }
   467  
   468  func TestNoHelperGoroutines(t *testing.T) {
   469  	output := runTestProg(t, "testprog", "NoHelperGoroutines")
   470  	matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1)
   471  	if len(matches) != 1 || matches[0][0] != "goroutine 1 [" {
   472  		t.Fatalf("want to see only goroutine 1, see:\n%s", output)
   473  	}
   474  }
   475  
   476  func TestBreakpoint(t *testing.T) {
   477  	output := runTestProg(t, "testprog", "Breakpoint")
   478  	// If runtime.Breakpoint() is inlined, then the stack trace prints
   479  	// "runtime.Breakpoint(...)" instead of "runtime.Breakpoint()".
   480  	want := "runtime.Breakpoint("
   481  	if !strings.Contains(output, want) {
   482  		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
   483  	}
   484  }
   485  
   486  func TestGoexitInPanic(t *testing.T) {
   487  	// External linking brings in cgo, causing deadlock detection not working.
   488  	testenv.MustInternalLink(t, deadlockBuildTypes)
   489  
   490  	// see issue 8774: this code used to trigger an infinite recursion
   491  	output := runTestProg(t, "testprog", "GoexitInPanic")
   492  	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
   493  	if !strings.HasPrefix(output, want) {
   494  		t.Fatalf("output does not start with %q:\n%s", want, output)
   495  	}
   496  }
   497  
   498  // Issue 14965: Runtime panics should be of type runtime.Error
   499  func TestRuntimePanicWithRuntimeError(t *testing.T) {
   500  	testCases := [...]func(){
   501  		0: func() {
   502  			var m map[uint64]bool
   503  			m[1234] = true
   504  		},
   505  		1: func() {
   506  			ch := make(chan struct{})
   507  			close(ch)
   508  			close(ch)
   509  		},
   510  		2: func() {
   511  			var ch = make(chan struct{})
   512  			close(ch)
   513  			ch <- struct{}{}
   514  		},
   515  		3: func() {
   516  			var s = make([]int, 2)
   517  			_ = s[2]
   518  		},
   519  		4: func() {
   520  			n := -1
   521  			_ = make(chan bool, n)
   522  		},
   523  		5: func() {
   524  			close((chan bool)(nil))
   525  		},
   526  	}
   527  
   528  	for i, fn := range testCases {
   529  		got := panicValue(fn)
   530  		if _, ok := got.(runtime.Error); !ok {
   531  			t.Errorf("test #%d: recovered value %v(type %T) does not implement runtime.Error", i, got, got)
   532  		}
   533  	}
   534  }
   535  
   536  func panicValue(fn func()) (recovered any) {
   537  	defer func() {
   538  		recovered = recover()
   539  	}()
   540  	fn()
   541  	return
   542  }
   543  
   544  func TestPanicAfterGoexit(t *testing.T) {
   545  	// an uncaught panic should still work after goexit
   546  	output := runTestProg(t, "testprog", "PanicAfterGoexit")
   547  	want := "panic: hello"
   548  	if !strings.HasPrefix(output, want) {
   549  		t.Fatalf("output does not start with %q:\n%s", want, output)
   550  	}
   551  }
   552  
   553  func TestRecoveredPanicAfterGoexit(t *testing.T) {
   554  	// External linking brings in cgo, causing deadlock detection not working.
   555  	testenv.MustInternalLink(t, deadlockBuildTypes)
   556  
   557  	output := runTestProg(t, "testprog", "RecoveredPanicAfterGoexit")
   558  	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
   559  	if !strings.HasPrefix(output, want) {
   560  		t.Fatalf("output does not start with %q:\n%s", want, output)
   561  	}
   562  }
   563  
   564  func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
   565  	// External linking brings in cgo, causing deadlock detection not working.
   566  	testenv.MustInternalLink(t, deadlockBuildTypes)
   567  
   568  	t.Parallel()
   569  	output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit")
   570  	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
   571  	if !strings.HasPrefix(output, want) {
   572  		t.Fatalf("output does not start with %q:\n%s", want, output)
   573  	}
   574  }
   575  
   576  func TestRecoverBeforePanicAfterGoexit2(t *testing.T) {
   577  	// External linking brings in cgo, causing deadlock detection not working.
   578  	testenv.MustInternalLink(t, deadlockBuildTypes)
   579  
   580  	t.Parallel()
   581  	output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit2")
   582  	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
   583  	if !strings.HasPrefix(output, want) {
   584  		t.Fatalf("output does not start with %q:\n%s", want, output)
   585  	}
   586  }
   587  
   588  func TestNetpollDeadlock(t *testing.T) {
   589  	t.Parallel()
   590  	output := runTestProg(t, "testprognet", "NetpollDeadlock")
   591  	want := "done\n"
   592  	if !strings.HasSuffix(output, want) {
   593  		t.Fatalf("output does not start with %q:\n%s", want, output)
   594  	}
   595  }
   596  
   597  func TestPanicTraceback(t *testing.T) {
   598  	t.Parallel()
   599  	output := runTestProg(t, "testprog", "PanicTraceback")
   600  	want := "panic: hello\n\tpanic: panic pt2\n\tpanic: panic pt1\n"
   601  	if !strings.HasPrefix(output, want) {
   602  		t.Fatalf("output does not start with %q:\n%s", want, output)
   603  	}
   604  
   605  	// Check functions in the traceback.
   606  	fns := []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"}
   607  	for _, fn := range fns {
   608  		re := regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `\(.*\n`)
   609  		idx := re.FindStringIndex(output)
   610  		if idx == nil {
   611  			t.Fatalf("expected %q function in traceback:\n%s", fn, output)
   612  		}
   613  		output = output[idx[1]:]
   614  	}
   615  }
   616  
   617  func testPanicDeadlock(t *testing.T, name string, want string) {
   618  	// test issue 14432
   619  	output := runTestProg(t, "testprog", name)
   620  	if !strings.HasPrefix(output, want) {
   621  		t.Fatalf("output does not start with %q:\n%s", want, output)
   622  	}
   623  }
   624  
   625  func TestPanicDeadlockGosched(t *testing.T) {
   626  	testPanicDeadlock(t, "GoschedInPanic", "panic: errorThatGosched\n\n")
   627  }
   628  
   629  func TestPanicDeadlockSyscall(t *testing.T) {
   630  	testPanicDeadlock(t, "SyscallInPanic", "1\n2\npanic: 3\n\n")
   631  }
   632  
   633  func TestPanicLoop(t *testing.T) {
   634  	output := runTestProg(t, "testprog", "PanicLoop")
   635  	if want := "panic while printing panic value"; !strings.Contains(output, want) {
   636  		t.Errorf("output does not contain %q:\n%s", want, output)
   637  	}
   638  }
   639  
   640  func TestMemPprof(t *testing.T) {
   641  	testenv.MustHaveGoRun(t)
   642  
   643  	exe, err := buildTestProg(t, "testprog")
   644  	if err != nil {
   645  		t.Fatal(err)
   646  	}
   647  
   648  	got, err := testenv.CleanCmdEnv(exec.Command(exe, "MemProf")).CombinedOutput()
   649  	if err != nil {
   650  		t.Fatalf("testprog failed: %s, output:\n%s", err, got)
   651  	}
   652  	fn := strings.TrimSpace(string(got))
   653  	defer os.Remove(fn)
   654  
   655  	for try := 0; try < 2; try++ {
   656  		cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-alloc_space", "-top"))
   657  		// Check that pprof works both with and without explicit executable on command line.
   658  		if try == 0 {
   659  			cmd.Args = append(cmd.Args, exe, fn)
   660  		} else {
   661  			cmd.Args = append(cmd.Args, fn)
   662  		}
   663  		found := false
   664  		for i, e := range cmd.Env {
   665  			if strings.HasPrefix(e, "PPROF_TMPDIR=") {
   666  				cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
   667  				found = true
   668  				break
   669  			}
   670  		}
   671  		if !found {
   672  			cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
   673  		}
   674  
   675  		top, err := cmd.CombinedOutput()
   676  		t.Logf("%s:\n%s", cmd.Args, top)
   677  		if err != nil {
   678  			t.Error(err)
   679  		} else if !bytes.Contains(top, []byte("MemProf")) {
   680  			t.Error("missing MemProf in pprof output")
   681  		}
   682  	}
   683  }
   684  
   685  var concurrentMapTest = flag.Bool("run_concurrent_map_tests", false, "also run flaky concurrent map tests")
   686  
   687  func TestConcurrentMapWrites(t *testing.T) {
   688  	if !*concurrentMapTest {
   689  		t.Skip("skipping without -run_concurrent_map_tests")
   690  	}
   691  	if race.Enabled {
   692  		t.Skip("skipping test: -race will catch the race, this test is for the built-in race detection")
   693  	}
   694  	testenv.MustHaveGoRun(t)
   695  	output := runTestProg(t, "testprog", "concurrentMapWrites")
   696  	want := "fatal error: concurrent map writes\n"
   697  	// Concurrent writes can corrupt the map in a way that we
   698  	// detect with a separate throw.
   699  	want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
   700  	if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
   701  		t.Fatalf("output does not start with %q:\n%s", want, output)
   702  	}
   703  }
   704  func TestConcurrentMapReadWrite(t *testing.T) {
   705  	if !*concurrentMapTest {
   706  		t.Skip("skipping without -run_concurrent_map_tests")
   707  	}
   708  	if race.Enabled {
   709  		t.Skip("skipping test: -race will catch the race, this test is for the built-in race detection")
   710  	}
   711  	testenv.MustHaveGoRun(t)
   712  	output := runTestProg(t, "testprog", "concurrentMapReadWrite")
   713  	want := "fatal error: concurrent map read and map write\n"
   714  	// Concurrent writes can corrupt the map in a way that we
   715  	// detect with a separate throw.
   716  	want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
   717  	if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
   718  		t.Fatalf("output does not start with %q:\n%s", want, output)
   719  	}
   720  }
   721  func TestConcurrentMapIterateWrite(t *testing.T) {
   722  	if !*concurrentMapTest {
   723  		t.Skip("skipping without -run_concurrent_map_tests")
   724  	}
   725  	if race.Enabled {
   726  		t.Skip("skipping test: -race will catch the race, this test is for the built-in race detection")
   727  	}
   728  	testenv.MustHaveGoRun(t)
   729  	output := runTestProg(t, "testprog", "concurrentMapIterateWrite")
   730  	want := "fatal error: concurrent map iteration and map write\n"
   731  	// Concurrent writes can corrupt the map in a way that we
   732  	// detect with a separate throw.
   733  	want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
   734  	if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
   735  		t.Fatalf("output does not start with %q:\n%s", want, output)
   736  	}
   737  }
   738  
   739  func TestConcurrentMapWritesIssue69447(t *testing.T) {
   740  	testenv.MustHaveGoRun(t)
   741  	if race.Enabled {
   742  		t.Skip("skipping test: -race will catch the race, this test is for the built-in race detection")
   743  	}
   744  	exe, err := buildTestProg(t, "testprog")
   745  	if err != nil {
   746  		t.Fatal(err)
   747  	}
   748  	for i := 0; i < 200; i++ {
   749  		output := runBuiltTestProg(t, exe, "concurrentMapWrites")
   750  		if output == "" {
   751  			// If we didn't detect an error, that's ok.
   752  			// This case makes this test not flaky like
   753  			// the other ones above.
   754  			// (More correctly, this case makes this test flaky
   755  			// in the other direction, in that it might not
   756  			// detect a problem even if there is one.)
   757  			continue
   758  		}
   759  		want := "fatal error: concurrent map writes\n"
   760  		// Concurrent writes can corrupt the map in a way that we
   761  		// detect with a separate throw.
   762  		want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
   763  		if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
   764  			t.Fatalf("output does not start with %q:\n%s", want, output)
   765  		}
   766  	}
   767  }
   768  
   769  type point struct {
   770  	x, y *int
   771  }
   772  
   773  func (p *point) negate() {
   774  	*p.x = *p.x * -1
   775  	*p.y = *p.y * -1
   776  }
   777  
   778  // Test for issue #10152.
   779  func TestPanicInlined(t *testing.T) {
   780  	defer func() {
   781  		r := recover()
   782  		if r == nil {
   783  			t.Fatalf("recover failed")
   784  		}
   785  		buf := make([]byte, 2048)
   786  		n := runtime.Stack(buf, false)
   787  		buf = buf[:n]
   788  		if !bytes.Contains(buf, []byte("(*point).negate(")) {
   789  			t.Fatalf("expecting stack trace to contain call to (*point).negate()")
   790  		}
   791  	}()
   792  
   793  	pt := new(point)
   794  	pt.negate()
   795  }
   796  
   797  // Test for issues #3934 and #20018.
   798  // We want to delay exiting until a panic print is complete.
   799  func TestPanicRace(t *testing.T) {
   800  	testenv.MustHaveGoRun(t)
   801  
   802  	exe, err := buildTestProg(t, "testprog")
   803  	if err != nil {
   804  		t.Fatal(err)
   805  	}
   806  
   807  	// The test is intentionally racy, and in my testing does not
   808  	// produce the expected output about 0.05% of the time.
   809  	// So run the program in a loop and only fail the test if we
   810  	// get the wrong output ten times in a row.
   811  	const tries = 10
   812  retry:
   813  	for i := 0; i < tries; i++ {
   814  		got, err := testenv.CleanCmdEnv(exec.Command(exe, "PanicRace")).CombinedOutput()
   815  		if err == nil {
   816  			t.Logf("try %d: program exited successfully, should have failed", i+1)
   817  			continue
   818  		}
   819  
   820  		if i > 0 {
   821  			t.Logf("try %d:\n", i+1)
   822  		}
   823  		t.Logf("%s\n", got)
   824  
   825  		wants := []string{
   826  			"panic: crash",
   827  			"PanicRace",
   828  			"created by ",
   829  		}
   830  		for _, want := range wants {
   831  			if !bytes.Contains(got, []byte(want)) {
   832  				t.Logf("did not find expected string %q", want)
   833  				continue retry
   834  			}
   835  		}
   836  
   837  		// Test generated expected output.
   838  		return
   839  	}
   840  	t.Errorf("test ran %d times without producing expected output", tries)
   841  }
   842  
   843  func TestBadTraceback(t *testing.T) {
   844  	if asan.Enabled || msan.Enabled || race.Enabled {
   845  		t.Skip("skipped test: checkptr mode catches the corruption")
   846  	}
   847  	output := runTestProg(t, "testprog", "BadTraceback")
   848  	for _, want := range []string{
   849  		"unexpected return pc",
   850  		"called from 0xbad",
   851  		"00000bad",    // Smashed LR in hex dump
   852  		"<main.badLR", // Symbolization in hex dump (badLR1 or badLR2)
   853  	} {
   854  		if !strings.Contains(output, want) {
   855  			t.Errorf("output does not contain %q:\n%s", want, output)
   856  		}
   857  	}
   858  }
   859  
   860  func TestTimePprof(t *testing.T) {
   861  	// This test is unreliable on any system in which nanotime
   862  	// calls into libc.
   863  	switch runtime.GOOS {
   864  	case "aix", "darwin", "illumos", "openbsd", "solaris":
   865  		t.Skipf("skipping on %s because nanotime calls libc", runtime.GOOS)
   866  	}
   867  	if race.Enabled || asan.Enabled || msan.Enabled {
   868  		t.Skip("skipping on sanitizers because the sanitizer runtime is external code")
   869  	}
   870  
   871  	// Pass GOTRACEBACK for issue #41120 to try to get more
   872  	// information on timeout.
   873  	fn := runTestProg(t, "testprog", "TimeProf", "GOTRACEBACK=crash")
   874  	fn = strings.TrimSpace(fn)
   875  	defer os.Remove(fn)
   876  
   877  	cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1", fn))
   878  	cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
   879  	top, err := cmd.CombinedOutput()
   880  	t.Logf("%s", top)
   881  	if err != nil {
   882  		t.Error(err)
   883  	} else if bytes.Contains(top, []byte("ExternalCode")) {
   884  		t.Error("profiler refers to ExternalCode")
   885  	}
   886  }
   887  
   888  // Test that runtime.abort does so.
   889  func TestAbort(t *testing.T) {
   890  	// Pass GOTRACEBACK to ensure we get runtime frames.
   891  	output := runTestProg(t, "testprog", "Abort", "GOTRACEBACK=system")
   892  	if want := "runtime.abort"; !strings.Contains(output, want) {
   893  		t.Errorf("output does not contain %q:\n%s", want, output)
   894  	}
   895  	if strings.Contains(output, "BAD") {
   896  		t.Errorf("output contains BAD:\n%s", output)
   897  	}
   898  	// Check that it's a signal traceback.
   899  	want := "PC="
   900  	// For systems that use a breakpoint, check specifically for that.
   901  	switch runtime.GOARCH {
   902  	case "386", "amd64":
   903  		switch runtime.GOOS {
   904  		case "plan9":
   905  			want = "sys: breakpoint"
   906  		case "windows":
   907  			want = "Exception 0x80000003"
   908  		default:
   909  			want = "SIGTRAP"
   910  		}
   911  	}
   912  	if !strings.Contains(output, want) {
   913  		t.Errorf("output does not contain %q:\n%s", want, output)
   914  	}
   915  }
   916  
   917  // For TestRuntimePanic: test a panic in the runtime package without
   918  // involving the testing harness.
   919  func init() {
   920  	if os.Getenv("GO_TEST_RUNTIME_PANIC") == "1" {
   921  		defer func() {
   922  			if r := recover(); r != nil {
   923  				// We expect to crash, so exit 0
   924  				// to indicate failure.
   925  				os.Exit(0)
   926  			}
   927  		}()
   928  		runtime.PanicForTesting(nil, 1)
   929  		// We expect to crash, so exit 0 to indicate failure.
   930  		os.Exit(0)
   931  	}
   932  	if os.Getenv("GO_TEST_RUNTIME_NPE_READMEMSTATS") == "1" {
   933  		runtime.ReadMemStats(nil)
   934  		os.Exit(0)
   935  	}
   936  	if os.Getenv("GO_TEST_RUNTIME_NPE_FUNCMETHOD") == "1" {
   937  		var f *runtime.Func
   938  		_ = f.Entry()
   939  		os.Exit(0)
   940  	}
   941  
   942  }
   943  
   944  func TestRuntimePanic(t *testing.T) {
   945  	cmd := testenv.CleanCmdEnv(exec.Command(testenv.Executable(t), "-test.run=^TestRuntimePanic$"))
   946  	cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_PANIC=1")
   947  	out, err := cmd.CombinedOutput()
   948  	t.Logf("%s", out)
   949  	if err == nil {
   950  		t.Error("child process did not fail")
   951  	} else if want := "runtime.unexportedPanicForTesting"; !bytes.Contains(out, []byte(want)) {
   952  		t.Errorf("output did not contain expected string %q", want)
   953  	}
   954  }
   955  
   956  func TestTracebackRuntimeFunction(t *testing.T) {
   957  	cmd := testenv.CleanCmdEnv(exec.Command(testenv.Executable(t), "-test.run=^TestTracebackRuntimeFunction$"))
   958  	cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_READMEMSTATS=1")
   959  	out, err := cmd.CombinedOutput()
   960  	t.Logf("%s", out)
   961  	if err == nil {
   962  		t.Error("child process did not fail")
   963  	} else if want := "runtime.ReadMemStats"; !bytes.Contains(out, []byte(want)) {
   964  		t.Errorf("output did not contain expected string %q", want)
   965  	}
   966  }
   967  
   968  func TestTracebackRuntimeMethod(t *testing.T) {
   969  	cmd := testenv.CleanCmdEnv(exec.Command(testenv.Executable(t), "-test.run=^TestTracebackRuntimeMethod$"))
   970  	cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_FUNCMETHOD=1")
   971  	out, err := cmd.CombinedOutput()
   972  	t.Logf("%s", out)
   973  	if err == nil {
   974  		t.Error("child process did not fail")
   975  	} else if want := "runtime.(*Func).Entry"; !bytes.Contains(out, []byte(want)) {
   976  		t.Errorf("output did not contain expected string %q", want)
   977  	}
   978  }
   979  
   980  // Test that g0 stack overflows are handled gracefully.
   981  func TestG0StackOverflow(t *testing.T) {
   982  	if runtime.GOOS == "ios" {
   983  		testenv.SkipFlaky(t, 62671)
   984  	}
   985  
   986  	if os.Getenv("TEST_G0_STACK_OVERFLOW") != "1" {
   987  		cmd := testenv.CleanCmdEnv(testenv.Command(t, testenv.Executable(t), "-test.run=^TestG0StackOverflow$", "-test.v"))
   988  		cmd.Env = append(cmd.Env, "TEST_G0_STACK_OVERFLOW=1")
   989  		out, err := cmd.CombinedOutput()
   990  		t.Logf("output:\n%s", out)
   991  		// Don't check err since it's expected to crash.
   992  		if n := strings.Count(string(out), "morestack on g0\n"); n != 1 {
   993  			t.Fatalf("%s\n(exit status %v)", out, err)
   994  		}
   995  		if runtime.CrashStackImplemented {
   996  			// check for a stack trace
   997  			want := "runtime.stackOverflow"
   998  			if n := strings.Count(string(out), want); n < 5 {
   999  				t.Errorf("output does not contain %q at least 5 times:\n%s", want, out)
  1000  			}
  1001  			return // it's not a signal-style traceback
  1002  		}
  1003  		// Check that it's a signal-style traceback.
  1004  		if runtime.GOOS != "windows" {
  1005  			if want := "PC="; !strings.Contains(string(out), want) {
  1006  				t.Errorf("output does not contain %q:\n%s", want, out)
  1007  			}
  1008  		}
  1009  		return
  1010  	}
  1011  
  1012  	runtime.G0StackOverflow()
  1013  }
  1014  
  1015  // For TestCrashWhileTracing: test a panic without involving the testing
  1016  // harness, as we rely on stdout only containing trace output.
  1017  func init() {
  1018  	if os.Getenv("TEST_CRASH_WHILE_TRACING") == "1" {
  1019  		trace.Start(os.Stdout)
  1020  		trace.Log(context.Background(), "xyzzy-cat", "xyzzy-msg")
  1021  		panic("yzzyx")
  1022  	}
  1023  }
  1024  
  1025  func TestCrashWhileTracing(t *testing.T) {
  1026  	testenv.MustHaveExec(t)
  1027  
  1028  	cmd := testenv.CleanCmdEnv(testenv.Command(t, testenv.Executable(t)))
  1029  	cmd.Env = append(cmd.Env, "TEST_CRASH_WHILE_TRACING=1")
  1030  	stdOut, err := cmd.StdoutPipe()
  1031  	var errOut bytes.Buffer
  1032  	cmd.Stderr = &errOut
  1033  
  1034  	if err := cmd.Start(); err != nil {
  1035  		t.Fatalf("could not start subprocess: %v", err)
  1036  	}
  1037  	r, err := traceparse.NewReader(stdOut)
  1038  	if err != nil {
  1039  		t.Fatalf("could not create trace.NewReader: %v", err)
  1040  	}
  1041  	var seen bool
  1042  	nSync := 0
  1043  	i := 1
  1044  loop:
  1045  	for ; ; i++ {
  1046  		ev, err := r.ReadEvent()
  1047  		if err != nil {
  1048  			// We may have a broken tail to the trace -- that's OK.
  1049  			// We'll make sure we saw at least one complete generation.
  1050  			if err != io.EOF {
  1051  				t.Logf("error at event %d: %v", i, err)
  1052  			}
  1053  			break loop
  1054  		}
  1055  		switch ev.Kind() {
  1056  		case traceparse.EventSync:
  1057  			nSync = ev.Sync().N
  1058  		case traceparse.EventLog:
  1059  			v := ev.Log()
  1060  			if v.Category == "xyzzy-cat" && v.Message == "xyzzy-msg" {
  1061  				// Should we already stop reading here? More events may come, but
  1062  				// we're not guaranteeing a fully unbroken trace until the last
  1063  				// byte...
  1064  				seen = true
  1065  			}
  1066  		}
  1067  	}
  1068  	if err := cmd.Wait(); err == nil {
  1069  		t.Error("the process should have panicked")
  1070  	}
  1071  	if nSync <= 1 {
  1072  		t.Errorf("expected at least one full generation to have been emitted before the trace was considered broken")
  1073  	}
  1074  	if !seen {
  1075  		t.Errorf("expected one matching log event matching, but none of the %d received trace events match", i)
  1076  	}
  1077  	t.Logf("stderr output:\n%s", errOut.String())
  1078  	needle := "yzzyx\n"
  1079  	if n := strings.Count(errOut.String(), needle); n != 1 {
  1080  		t.Fatalf("did not find expected panic message %q\n(exit status %v)", needle, err)
  1081  	}
  1082  }
  1083  
  1084  // Test that panic message is not clobbered.
  1085  // See issue 30150.
  1086  func TestDoublePanic(t *testing.T) {
  1087  	output := runTestProg(t, "testprog", "DoublePanic", "GODEBUG=clobberfree=1")
  1088  	wants := []string{"panic: XXX", "panic: YYY"}
  1089  	for _, want := range wants {
  1090  		if !strings.Contains(output, want) {
  1091  			t.Errorf("output:\n%s\n\nwant output containing: %s", output, want)
  1092  		}
  1093  	}
  1094  }
  1095  
  1096  // Test that panic while panicking discards error message
  1097  // See issue 52257
  1098  func TestPanicWhilePanicking(t *testing.T) {
  1099  	tests := []struct {
  1100  		Want string
  1101  		Func string
  1102  	}{
  1103  		{
  1104  			"panic while printing panic value: important multi-line\n\terror message",
  1105  			"ErrorPanic",
  1106  		},
  1107  		{
  1108  			"panic while printing panic value: important multi-line\n\tstringer message",
  1109  			"StringerPanic",
  1110  		},
  1111  		{
  1112  			"panic while printing panic value: type",
  1113  			"DoubleErrorPanic",
  1114  		},
  1115  		{
  1116  			"panic while printing panic value: type",
  1117  			"DoubleStringerPanic",
  1118  		},
  1119  		{
  1120  			"panic while printing panic value: type",
  1121  			"CircularPanic",
  1122  		},
  1123  		{
  1124  			"important multi-line\n\tstring message",
  1125  			"StringPanic",
  1126  		},
  1127  		{
  1128  			"nil",
  1129  			"NilPanic",
  1130  		},
  1131  	}
  1132  	for _, x := range tests {
  1133  		output := runTestProg(t, "testprog", x.Func)
  1134  		if !strings.Contains(output, x.Want) {
  1135  			t.Errorf("output does not contain %q:\n%s", x.Want, output)
  1136  		}
  1137  	}
  1138  }
  1139  
  1140  func TestPanicOnUnsafeSlice(t *testing.T) {
  1141  	output := runTestProg(t, "testprog", "panicOnNilAndEleSizeIsZero")
  1142  	// Note: This is normally a panic, but is a throw when checkptr is
  1143  	// enabled.
  1144  	want := "unsafe.Slice: ptr is nil and len is not zero"
  1145  	if !strings.Contains(output, want) {
  1146  		t.Errorf("output does not contain %q:\n%s", want, output)
  1147  	}
  1148  }
  1149  
  1150  func TestNetpollWaiters(t *testing.T) {
  1151  	t.Parallel()
  1152  	output := runTestProg(t, "testprognet", "NetpollWaiters")
  1153  	want := "OK\n"
  1154  	if output != want {
  1155  		t.Fatalf("output is not %q\n%s", want, output)
  1156  	}
  1157  }
  1158  
  1159  func TestFinalizerOrCleanupDeadlock(t *testing.T) {
  1160  	t.Parallel()
  1161  
  1162  	for _, useCleanup := range []bool{false, true} {
  1163  		progName := "Finalizer"
  1164  		want := "runtime.runFinalizers"
  1165  		if useCleanup {
  1166  			progName = "Cleanup"
  1167  			want = "runtime.runCleanups"
  1168  		}
  1169  
  1170  		// The runtime.runFinalizers/runtime.runCleanups frame should appear in panics, even if
  1171  		// runtime frames are normally hidden (GOTRACEBACK=all).
  1172  		t.Run("Panic", func(t *testing.T) {
  1173  			t.Parallel()
  1174  			output := runTestProg(t, "testprog", progName+"Deadlock", "GOTRACEBACK=all", "GO_TEST_FINALIZER_DEADLOCK=panic")
  1175  			want := want + "()"
  1176  			if !strings.Contains(output, want) {
  1177  				t.Errorf("output does not contain %q:\n%s", want, output)
  1178  			}
  1179  		})
  1180  
  1181  		// The runtime.runFinalizers/runtime.Cleanups frame should appear in runtime.Stack,
  1182  		// even though runtime frames are normally hidden.
  1183  		t.Run("Stack", func(t *testing.T) {
  1184  			t.Parallel()
  1185  			output := runTestProg(t, "testprog", progName+"Deadlock", "GO_TEST_FINALIZER_DEADLOCK=stack")
  1186  			want := want + "()"
  1187  			if !strings.Contains(output, want) {
  1188  				t.Errorf("output does not contain %q:\n%s", want, output)
  1189  			}
  1190  		})
  1191  
  1192  		// The runtime.runFinalizers/runtime.Cleanups frame should appear in goroutine
  1193  		// profiles.
  1194  		t.Run("PprofProto", func(t *testing.T) {
  1195  			t.Parallel()
  1196  			output := runTestProg(t, "testprog", progName+"Deadlock", "GO_TEST_FINALIZER_DEADLOCK=pprof_proto")
  1197  
  1198  			p, err := profile.Parse(strings.NewReader(output))
  1199  			if err != nil {
  1200  				// Logging the binary proto data is not very nice, but it might
  1201  				// be a text error message instead.
  1202  				t.Logf("Output: %s", output)
  1203  				t.Fatalf("Error parsing proto output: %v", err)
  1204  			}
  1205  			for _, s := range p.Sample {
  1206  				for _, loc := range s.Location {
  1207  					for _, line := range loc.Line {
  1208  						if line.Function.Name == want {
  1209  							// Done!
  1210  							return
  1211  						}
  1212  					}
  1213  				}
  1214  			}
  1215  			t.Errorf("Profile does not contain %q:\n%s", want, p)
  1216  		})
  1217  
  1218  		// The runtime.runFinalizers/runtime.runCleanups frame should appear in goroutine
  1219  		// profiles (debug=1).
  1220  		t.Run("PprofDebug1", func(t *testing.T) {
  1221  			t.Parallel()
  1222  			output := runTestProg(t, "testprog", progName+"Deadlock", "GO_TEST_FINALIZER_DEADLOCK=pprof_debug1")
  1223  			want := want + "+"
  1224  			if !strings.Contains(output, want) {
  1225  				t.Errorf("output does not contain %q:\n%s", want, output)
  1226  			}
  1227  		})
  1228  
  1229  		// The runtime.runFinalizers/runtime.runCleanups frame should appear in goroutine
  1230  		// profiles (debug=2).
  1231  		t.Run("PprofDebug2", func(t *testing.T) {
  1232  			t.Parallel()
  1233  			output := runTestProg(t, "testprog", progName+"Deadlock", "GO_TEST_FINALIZER_DEADLOCK=pprof_debug2")
  1234  			want := want + "()"
  1235  			if !strings.Contains(output, want) {
  1236  				t.Errorf("output does not contain %q:\n%s", want, output)
  1237  			}
  1238  		})
  1239  	}
  1240  }
  1241  
  1242  func TestSynctestCondSignalFromNoBubble(t *testing.T) {
  1243  	for _, test := range []string{
  1244  		"SynctestCond/signal/no_bubble",
  1245  		"SynctestCond/broadcast/no_bubble",
  1246  		"SynctestCond/signal/other_bubble",
  1247  		"SynctestCond/broadcast/other_bubble",
  1248  	} {
  1249  		t.Run(test, func(t *testing.T) {
  1250  			output := runTestProg(t, "testprog", test)
  1251  			want := "fatal error: semaphore wake of synctest goroutine from outside bubble"
  1252  			if !strings.Contains(output, want) {
  1253  				t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
  1254  			}
  1255  		})
  1256  	}
  1257  }
  1258  

View as plain text