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

View as plain text