Пример #1
0
// Fork can hang if preempted with signals frequently enough (see issue 5517).
// Ensure that we do not do this.
func TestCPUProfileWithFork(t *testing.T) {
	testenv.MustHaveExec(t)

	heap := 1 << 30
	if runtime.GOOS == "android" {
		// Use smaller size for Android to avoid crash.
		heap = 100 << 20
	}
	if testing.Short() {
		heap = 100 << 20
	}
	// This makes fork slower.
	garbage := make([]byte, heap)
	// Need to touch the slice, otherwise it won't be paged in.
	done := make(chan bool)
	go func() {
		for i := range garbage {
			garbage[i] = 42
		}
		done <- true
	}()
	<-done

	var prof bytes.Buffer
	if err := StartCPUProfile(&prof); err != nil {
		t.Fatal(err)
	}
	defer StopCPUProfile()

	for i := 0; i < 10; i++ {
		exec.Command(os.Args[0], "-h").CombinedOutput()
	}
}
Пример #2
0
func TestStackBarrierProfiling(t *testing.T) {
	if !strings.Contains(os.Getenv("GODEBUG"), "gcstackbarrierall=1") {
		// Re-execute this test with constant GC and stack
		// barriers at every frame.
		testenv.MustHaveExec(t)
		if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" {
			t.Skip("gcstackbarrierall doesn't work on ppc64")
		}
		cmd := exec.Command(os.Args[0], "-test.run=TestStackBarrierProfiling")
		cmd.Env = append([]string{"GODEBUG=gcstackbarrierall=1", "GOGC=1"}, os.Environ()...)
		if out, err := cmd.CombinedOutput(); err != nil {
			t.Fatalf("subprocess failed with %v:\n%s", err, out)
		}
		return
	}

	testCPUProfile(t, nil, func() {
		// This is long enough that we're likely to get one or
		// two samples in stackBarrier.
		duration := 5 * time.Second
		if testing.Short() {
			duration = 1 * time.Second
		}
		t := time.After(duration)
		for {
			deepStack(1000)
			select {
			case <-t:
				return
			default:
			}
		}
	})
}
Пример #3
0
func TestCommandRelativeName(t *testing.T) {
	testenv.MustHaveExec(t)

	// Run our own binary as a relative path
	// (e.g. "_test/exec.test") our parent directory.
	base := filepath.Base(os.Args[0]) // "exec.test"
	dir := filepath.Dir(os.Args[0])   // "/tmp/go-buildNNNN/os/exec/_test"
	if dir == "." {
		t.Skip("skipping; running test at root somehow")
	}
	parentDir := filepath.Dir(dir) // "/tmp/go-buildNNNN/os/exec"
	dirBase := filepath.Base(dir)  // "_test"
	if dirBase == "." {
		t.Skipf("skipping; unexpected shallow dir of %q", dir)
	}

	cmd := exec.Command(filepath.Join(dirBase, base), "-test.run=TestHelperProcess", "--", "echo", "foo")
	cmd.Dir = parentDir
	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}

	out, err := cmd.Output()
	if err != nil {
		t.Errorf("echo: %v", err)
	}
	if g, e := string(out), "foo\n"; g != e {
		t.Errorf("echo: want %q, got %q", e, g)
	}
}
Пример #4
0
func TestHostname(t *testing.T) {
	// There is no other way to fetch hostname on windows, but via winapi.
	// On Plan 9 it can be taken from #c/sysname as Hostname() does.
	switch runtime.GOOS {
	case "android", "plan9":
		t.Skipf("%s doesn't have /bin/hostname", runtime.GOOS)
	case "windows":
		testWindowsHostname(t)
		return
	}

	testenv.MustHaveExec(t)

	// Check internal Hostname() against the output of /bin/hostname.
	// Allow that the internal Hostname returns a Fully Qualified Domain Name
	// and the /bin/hostname only returns the first component
	hostname, err := Hostname()
	if err != nil {
		t.Fatalf("%v", err)
	}
	want := runBinHostname(t)
	if hostname != want {
		i := strings.Index(hostname, ".")
		if i < 0 || hostname[0:i] != want {
			t.Errorf("Hostname() = %q, want %q", hostname, want)
		}
	}
}
Пример #5
0
func TestStatStdin(t *testing.T) {
	switch runtime.GOOS {
	case "android", "plan9":
		t.Skipf("%s doesn't have /bin/sh", runtime.GOOS)
	}

	testenv.MustHaveExec(t)

	if Getenv("GO_WANT_HELPER_PROCESS") == "1" {
		st, err := Stdin.Stat()
		if err != nil {
			t.Fatalf("Stat failed: %v", err)
		}
		fmt.Println(st.Mode() & ModeNamedPipe)
		Exit(0)
	}

	var cmd *osexec.Cmd
	if runtime.GOOS == "windows" {
		cmd = osexec.Command("cmd", "/c", "echo output | "+Args[0]+" -test.run=TestStatStdin")
	} else {
		cmd = osexec.Command("/bin/sh", "-c", "echo output | "+Args[0]+" -test.run=TestStatStdin")
	}
	cmd.Env = append(Environ(), "GO_WANT_HELPER_PROCESS=1")

	output, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("Failed to spawn child process: %v %q", err, string(output))
	}

	// result will be like "prw-rw-rw"
	if len(output) < 1 || output[0] != 'p' {
		t.Fatalf("Child process reports stdin is not pipe '%v'", string(output))
	}
}
Пример #6
0
func TestGetppid(t *testing.T) {
	if runtime.GOOS == "plan9" {
		// TODO: golang.org/issue/8206
		t.Skipf("skipping test on plan9; see issue 8206")
	}

	testenv.MustHaveExec(t)

	if Getenv("GO_WANT_HELPER_PROCESS") == "1" {
		fmt.Print(Getppid())
		Exit(0)
	}

	cmd := osexec.Command(Args[0], "-test.run=TestGetppid")
	cmd.Env = append(Environ(), "GO_WANT_HELPER_PROCESS=1")

	// verify that Getppid() from the forked process reports our process id
	output, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("Failed to spawn child process: %v %q", err, string(output))
	}

	childPpid := string(output)
	ourPid := fmt.Sprintf("%d", Getpid())
	if childPpid != ourPid {
		t.Fatalf("Child process reports parent process id '%v', expected '%v'", childPpid, ourPid)
	}
}
Пример #7
0
func TestMutexMisuse(t *testing.T) {
	testenv.MustHaveExec(t)
	for _, test := range misuseTests {
		out, err := exec.Command(os.Args[0], "TESTMISUSE", test.name).CombinedOutput()
		if err == nil || !strings.Contains(string(out), "unlocked") {
			t.Errorf("%s: did not find failure with message about unlocked lock: %s\n%s\n", test.name, err, out)
		}
	}
}
Пример #8
0
func TestStackBarrierProfiling(t *testing.T) {
	if (runtime.GOOS == "linux" && runtime.GOARCH == "arm") ||
		runtime.GOOS == "openbsd" ||
		runtime.GOOS == "solaris" ||
		runtime.GOOS == "dragonfly" ||
		runtime.GOOS == "freebsd" {
		// This test currently triggers a large number of
		// usleep(100)s. These kernels/arches have poor
		// resolution timers, so this gives up a whole
		// scheduling quantum. On Linux and the BSDs (and
		// probably Solaris), profiling signals are only
		// generated when a process completes a whole
		// scheduling quantum, so this test often gets zero
		// profiling signals and fails.
		t.Skipf("low resolution timers inhibit profiling signals (golang.org/issue/13405)")
		return
	}

	if runtime.GOOS == "linux" && strings.HasPrefix(runtime.GOARCH, "mips") {
		if !haveLinuxHiresTimers() {
			t.Skipf("low resolution timers inhibit profiling signals (golang.org/issue/13405, golang.org/issue/17936)")
		}
	}

	if !strings.Contains(os.Getenv("GODEBUG"), "gcstackbarrierall=1") {
		// Re-execute this test with constant GC and stack
		// barriers at every frame.
		testenv.MustHaveExec(t)
		if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" {
			t.Skip("gcstackbarrierall doesn't work on ppc64")
		}
		args := []string{"-test.run=TestStackBarrierProfiling"}
		if testing.Short() {
			args = append(args, "-test.short")
		}
		cmd := exec.Command(os.Args[0], args...)
		cmd.Env = append([]string{"GODEBUG=gcstackbarrierall=1", "GOGC=1", "GOTRACEBACK=system"}, os.Environ()...)
		if out, err := cmd.CombinedOutput(); err != nil {
			t.Fatalf("subprocess failed with %v:\n%s", err, out)
		}
		return
	}

	testCPUProfile(t, nil, func(duration time.Duration) {
		// In long mode, we're likely to get one or two
		// samples in stackBarrier.
		t := time.After(duration)
		for {
			deepStack(1000)
			select {
			case <-t:
				return
			default:
			}
		}
	})
}
Пример #9
0
func helperCommand(t *testing.T, s ...string) *exec.Cmd {
	testenv.MustHaveExec(t)

	cs := []string{"-test.run=TestHelperProcess", "--"}
	cs = append(cs, s...)
	cmd := exec.Command(os.Args[0], cs...)
	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
	return cmd
}
Пример #10
0
// Check that permuting child process fds doesn't interfere with
// reporting of fork/exec status. See Issue 14979.
func TestExecErrPermutedFds(t *testing.T) {
	testenv.MustHaveExec(t)

	attr := &os.ProcAttr{Files: []*os.File{os.Stdin, os.Stderr, os.Stdout}}
	_, err := os.StartProcess("/", []string{"/"}, attr)
	if err == nil {
		t.Fatalf("StartProcess of invalid program returned err = nil")
	}
}
Пример #11
0
func create(t *testing.T) *command {
	testenv.MustHaveExec(t)

	proc := exec.Command("cat")
	stdin, err := proc.StdinPipe()
	if err != nil {
		t.Fatal(err)
	}

	return &command{stdin, proc, t}
}
Пример #12
0
func helperCommandContext(t *testing.T, ctx context.Context, s ...string) (cmd *exec.Cmd) {
	testenv.MustHaveExec(t)

	cs := []string{"-test.run=TestHelperProcess", "--"}
	cs = append(cs, s...)
	if ctx != nil {
		cmd = exec.CommandContext(ctx, os.Args[0], cs...)
	} else {
		cmd = exec.Command(os.Args[0], cs...)
	}
	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
	return cmd
}
Пример #13
0
func helperCommand(t *testing.T, s ...string) *exec.Cmd {
	testenv.MustHaveExec(t)

	cs := []string{"-test.run=TestHelperProcess", "--"}
	cs = append(cs, s...)
	cmd := exec.Command(os.Args[0], cs...)
	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
	path := os.Getenv("LD_LIBRARY_PATH")
	if path != "" {
		cmd.Env = append(cmd.Env, "LD_LIBRARY_PATH="+path)
	}
	return cmd
}
Пример #14
0
func TestOutputStderrCapture(t *testing.T) {
	testenv.MustHaveExec(t)

	cmd := helperCommand(t, "stderrfail")
	_, err := cmd.Output()
	ee, ok := err.(*exec.ExitError)
	if !ok {
		t.Fatalf("Output error type = %T; want ExitError", err)
	}
	got := string(ee.Stderr)
	want := "some stderr text\n"
	if got != want {
		t.Errorf("ExitError.Stderr = %q; want %q", got, want)
	}
}
Пример #15
0
// Test that a child handler writing only headers works.
// golang.org/issue/7196
func TestChildOnlyHeaders(t *testing.T) {
	testenv.MustHaveExec(t)

	h := &Handler{
		Path: os.Args[0],
		Root: "/test.go",
		Args: []string{"-test.run=TestBeChildCGIProcess"},
	}
	expectedMap := map[string]string{
		"_body": "",
	}
	replay := runCgiTest(t, h, "GET /test.go?no-body=1 HTTP/1.0\nHost: example.com\n\n", expectedMap)
	if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
		t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
	}
}
Пример #16
0
func testKillProcess(t *testing.T, processKiller func(p *Process)) {
	testenv.MustHaveExec(t)

	// Re-exec the test binary itself to emulate "sleep 1".
	cmd := osexec.Command(Args[0], "-test.run", "TestSleep")
	err := cmd.Start()
	if err != nil {
		t.Fatalf("Failed to start test process: %v", err)
	}
	go func() {
		time.Sleep(100 * time.Millisecond)
		processKiller(cmd.Process)
	}()
	err = cmd.Wait()
	if err == nil {
		t.Errorf("Test process succeeded, but expected to fail")
	}
}
Пример #17
0
func TestStdPipe(t *testing.T) {
	testenv.MustHaveExec(t)
	r, w, err := os.Pipe()
	if err != nil {
		t.Fatal(err)
	}
	if err := r.Close(); err != nil {
		t.Fatal(err)
	}
	// Invoke the test program to run the test and write to a closed pipe.
	// If sig is false:
	// writing to stdout or stderr should cause an immediate SIGPIPE;
	// writing to descriptor 3 should fail with EPIPE and then exit 0.
	// If sig is true:
	// all writes should fail with EPIPE and then exit 0.
	for _, sig := range []bool{false, true} {
		for dest := 1; dest < 4; dest++ {
			cmd := osexec.Command(os.Args[0], "-test.run", "TestStdPipeHelper")
			cmd.Stdout = w
			cmd.Stderr = w
			cmd.ExtraFiles = []*os.File{w}
			cmd.Env = append(os.Environ(), fmt.Sprintf("GO_TEST_STD_PIPE_HELPER=%d", dest))
			if sig {
				cmd.Env = append(cmd.Env, "GO_TEST_STD_PIPE_HELPER_SIGNAL=1")
			}
			if err := cmd.Run(); err == nil {
				if !sig && dest < 3 {
					t.Errorf("unexpected success of write to closed pipe %d sig %t in child", dest, sig)
				}
			} else if ee, ok := err.(*osexec.ExitError); !ok {
				t.Errorf("unexpected exec error type %T: %v", err, err)
			} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
				t.Errorf("unexpected wait status type %T: %v", ee.Sys(), ee.Sys())
			} else if ws.Signaled() && ws.Signal() == syscall.SIGPIPE {
				if sig || dest > 2 {
					t.Errorf("unexpected SIGPIPE signal for descriptor %d sig %t", dest, sig)
				}
			} else {
				t.Errorf("unexpected exit status %v for descriptor %ds sig %t", err, dest, sig)
			}
		}
	}
}
Пример #18
0
// Issue 9173: ignore stdin pipe writes if the program completes successfully.
func TestIgnorePipeErrorOnSuccess(t *testing.T) {
	testenv.MustHaveExec(t)

	// We really only care about testing this on Unixy things.
	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
		t.Skipf("skipping test on %q", runtime.GOOS)
	}

	cmd := helperCommand(t, "echo", "foo")
	var out bytes.Buffer
	cmd.Stdin = strings.NewReader(strings.Repeat("x", 10<<20))
	cmd.Stdout = &out
	if err := cmd.Run(); err != nil {
		t.Fatal(err)
	}
	if got, want := out.String(), "foo\n"; got != want {
		t.Errorf("output = %q; want %q", got, want)
	}
}
Пример #19
0
// If there's an error copying the child's output to the parent, test
// that we kill the child.
func TestKillChildAfterCopyError(t *testing.T) {
	testenv.MustHaveExec(t)

	defer func() { testHookStartProcess = nil }()
	proc := make(chan *os.Process, 1)
	testHookStartProcess = func(p *os.Process) {
		proc <- p
	}

	h := &Handler{
		Path: os.Args[0],
		Root: "/test.go",
		Args: []string{"-test.run=TestBeChildCGIProcess"},
	}
	req, _ := http.NewRequest("GET", "http://example.com/test.cgi?write-forever=1", nil)
	rec := httptest.NewRecorder()
	var out bytes.Buffer
	const writeLen = 50 << 10
	rw := &customWriterRecorder{&limitWriter{&out, writeLen}, rec}

	donec := make(chan bool, 1)
	go func() {
		h.ServeHTTP(rw, req)
		donec <- true
	}()

	select {
	case <-donec:
		if out.Len() != writeLen || out.Bytes()[0] != 'a' {
			t.Errorf("unexpected output: %q", out.Bytes())
		}
	case <-time.After(5 * time.Second):
		t.Errorf("timeout. ServeHTTP hung and didn't kill the child process?")
		select {
		case p := <-proc:
			p.Kill()
			t.Logf("killed process")
		default:
			t.Logf("didn't kill process")
		}
	}
}
Пример #20
0
func TestClosePipeOnCopyError(t *testing.T) {
	testenv.MustHaveExec(t)

	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
		t.Skipf("skipping test on %s - no yes command", runtime.GOOS)
	}
	cmd := exec.Command("yes")
	cmd.Stdout = new(badWriter)
	c := make(chan int, 1)
	go func() {
		err := cmd.Run()
		if err == nil {
			t.Errorf("yes completed successfully")
		}
		c <- 1
	}()
	select {
	case <-c:
		// ok
	case <-time.After(5 * time.Second):
		t.Fatalf("yes got stuck writing to bad writer")
	}
}
Пример #21
0
func TestStartProcess(t *testing.T) {
	testenv.MustHaveExec(t)

	var dir, cmd string
	var args []string
	switch runtime.GOOS {
	case "android":
		t.Skip("android doesn't have /bin/pwd")
	case "windows":
		cmd = Getenv("COMSPEC")
		dir = Getenv("SystemRoot")
		args = []string{"/c", "cd"}
	default:
		cmd = "/bin/pwd"
		dir = "/"
		args = []string{}
	}
	cmddir, cmdbase := filepath.Split(cmd)
	args = append([]string{cmdbase}, args...)
	// Test absolute executable path.
	exec(t, dir, cmd, args, dir)
	// Test relative executable path.
	exec(t, cmddir, cmdbase, args, cmddir)
}
Пример #22
0
func TestExecutable(t *testing.T) {
	testenv.MustHaveExec(t) // will also execlude nacl, which doesn't support Executable anyway
	ep, err := os.Executable()
	if err != nil {
		switch goos := runtime.GOOS; goos {
		case "openbsd": // procfs is not mounted by default
			t.Skipf("Executable failed on %s: %v, expected", goos, err)
		}
		t.Fatalf("Executable failed: %v", err)
	}
	// we want fn to be of the form "dir/prog"
	dir := filepath.Dir(filepath.Dir(ep))
	fn, err := filepath.Rel(dir, ep)
	if err != nil {
		t.Fatalf("filepath.Rel: %v", err)
	}
	cmd := &osexec.Cmd{}
	// make child start with a relative program path
	cmd.Dir = dir
	cmd.Path = fn
	// forge argv[0] for child, so that we can verify we could correctly
	// get real path of the executable without influenced by argv[0].
	cmd.Args = []string{"-", "-test.run=XXXX"}
	cmd.Env = append(os.Environ(), fmt.Sprintf("%s=1", executable_EnvVar))
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("exec(self) failed: %v", err)
	}
	outs := string(out)
	if !filepath.IsAbs(outs) {
		t.Fatalf("Child returned %q, want an absolute path", out)
	}
	if !sameFile(outs, ep) {
		t.Fatalf("Child returned %q, not the same file as %q", out, ep)
	}
}
Пример #23
0
// This test is a CGI host (testing host.go) that runs its own binary
// as a child process testing the other half of CGI (child.go).
func TestHostingOurselves(t *testing.T) {
	testenv.MustHaveExec(t)

	h := &Handler{
		Path: os.Args[0],
		Root: "/test.go",
		Args: []string{"-test.run=TestBeChildCGIProcess"},
	}
	expectedMap := map[string]string{
		"test":                  "Hello CGI-in-CGI",
		"param-a":               "b",
		"param-foo":             "bar",
		"env-GATEWAY_INTERFACE": "CGI/1.1",
		"env-HTTP_HOST":         "example.com",
		"env-PATH_INFO":         "",
		"env-QUERY_STRING":      "foo=bar&a=b",
		"env-REMOTE_ADDR":       "1.2.3.4",
		"env-REMOTE_HOST":       "1.2.3.4",
		"env-REMOTE_PORT":       "1234",
		"env-REQUEST_METHOD":    "GET",
		"env-REQUEST_URI":       "/test.go?foo=bar&a=b",
		"env-SCRIPT_FILENAME":   os.Args[0],
		"env-SCRIPT_NAME":       "/test.go",
		"env-SERVER_NAME":       "example.com",
		"env-SERVER_PORT":       "80",
		"env-SERVER_SOFTWARE":   "go",
	}
	replay := runCgiTest(t, h, "GET /test.go?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)

	if expected, got := "text/html; charset=utf-8", replay.Header().Get("Content-Type"); got != expected {
		t.Errorf("got a Content-Type of %q; expected %q", got, expected)
	}
	if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
		t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
	}
}
Пример #24
0
func TestExtraFiles(t *testing.T) {
	testenv.MustHaveExec(t)

	if runtime.GOOS == "windows" {
		t.Skipf("skipping test on %q", runtime.GOOS)
	}

	// Ensure that file descriptors have not already been leaked into
	// our environment.
	if !testedAlreadyLeaked {
		testedAlreadyLeaked = true
		closeUnexpectedFds(t, "TestExtraFiles")
	}

	// Force network usage, to verify the epoll (or whatever) fd
	// doesn't leak to the child,
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer ln.Close()

	// Make sure duplicated fds don't leak to the child.
	f, err := ln.(*net.TCPListener).File()
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()
	ln2, err := net.FileListener(f)
	if err != nil {
		t.Fatal(err)
	}
	defer ln2.Close()

	// Force TLS root certs to be loaded (which might involve
	// cgo), to make sure none of that potential C code leaks fds.
	ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
	// quiet expected TLS handshake error "remote error: bad certificate"
	ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0)
	ts.StartTLS()
	defer ts.Close()
	_, err = http.Get(ts.URL)
	if err == nil {
		t.Errorf("success trying to fetch %s; want an error", ts.URL)
	}

	tf, err := ioutil.TempFile("", "")
	if err != nil {
		t.Fatalf("TempFile: %v", err)
	}
	defer os.Remove(tf.Name())
	defer tf.Close()

	const text = "Hello, fd 3!"
	_, err = tf.Write([]byte(text))
	if err != nil {
		t.Fatalf("Write: %v", err)
	}
	_, err = tf.Seek(0, os.SEEK_SET)
	if err != nil {
		t.Fatalf("Seek: %v", err)
	}

	c := helperCommand(t, "read3")
	var stdout, stderr bytes.Buffer
	c.Stdout = &stdout
	c.Stderr = &stderr
	c.ExtraFiles = []*os.File{tf}
	err = c.Run()
	if err != nil {
		t.Fatalf("Run: %v; stdout %q, stderr %q", err, stdout.Bytes(), stderr.Bytes())
	}
	if stdout.String() != text {
		t.Errorf("got stdout %q, stderr %q; want %q on stdout", stdout.String(), stderr.String(), text)
	}
}
Пример #25
0
// TestTraceSymbolize tests symbolization and that events has proper stacks.
// In particular that we strip bottom uninteresting frames like goexit,
// top uninteresting frames (runtime guts).
func TestTraceSymbolize(t *testing.T) {
	testenv.MustHaveExec(t)

	buf := new(bytes.Buffer)
	if err := Start(buf); err != nil {
		t.Fatalf("failed to start tracing: %v", err)
	}
	defer Stop() // in case of early return

	// Now we will do a bunch of things for which we verify stacks later.
	// It is impossible to ensure that a goroutine has actually blocked
	// on a channel, in a select or otherwise. So we kick off goroutines
	// that need to block first in the hope that while we are executing
	// the rest of the test, they will block.
	go func() {
		select {}
	}()
	go func() {
		var c chan int
		c <- 0
	}()
	go func() {
		var c chan int
		<-c
	}()
	done1 := make(chan bool)
	go func() {
		<-done1
	}()
	done2 := make(chan bool)
	go func() {
		done2 <- true
	}()
	c1 := make(chan int)
	c2 := make(chan int)
	go func() {
		select {
		case <-c1:
		case <-c2:
		}
	}()
	var mu sync.Mutex
	mu.Lock()
	go func() {
		mu.Lock()
		mu.Unlock()
	}()
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		wg.Wait()
	}()
	cv := sync.NewCond(&sync.Mutex{})
	go func() {
		cv.L.Lock()
		cv.Wait()
		cv.L.Unlock()
	}()
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("failed to listen: %v", err)
	}
	go func() {
		c, err := ln.Accept()
		if err != nil {
			t.Fatalf("failed to accept: %v", err)
		}
		c.Close()
	}()
	rp, wp, err := os.Pipe()
	if err != nil {
		t.Fatalf("failed to create a pipe: %v", err)
	}
	defer rp.Close()
	defer wp.Close()
	pipeReadDone := make(chan bool)
	go func() {
		var data [1]byte
		rp.Read(data[:])
		pipeReadDone <- true
	}()

	time.Sleep(time.Millisecond)
	runtime.GC()
	runtime.Gosched()
	time.Sleep(time.Millisecond) // the last chance for the goroutines above to block
	done1 <- true
	<-done2
	select {
	case c1 <- 0:
	case c2 <- 0:
	}
	mu.Unlock()
	wg.Done()
	cv.Signal()
	c, err := net.Dial("tcp", ln.Addr().String())
	if err != nil {
		t.Fatalf("failed to dial: %v", err)
	}
	c.Close()
	var data [1]byte
	wp.Write(data[:])
	<-pipeReadDone

	Stop()
	events, _, err := parseTrace(t, buf)
	if err != nil {
		t.Fatalf("failed to parse trace: %v", err)
	}
	err = trace.Symbolize(events, os.Args[0])
	if err != nil {
		t.Fatalf("failed to symbolize trace: %v", err)
	}

	// Now check that the stacks are correct.
	type frame struct {
		Fn   string
		Line int
	}
	type eventDesc struct {
		Type byte
		Stk  []frame
	}
	want := []eventDesc{
		eventDesc{trace.EvGCStart, []frame{
			frame{"runtime.GC", 0},
			frame{"runtime/trace_test.TestTraceSymbolize", 106},
			frame{"testing.tRunner", 0},
		}},
		eventDesc{trace.EvGoSched, []frame{
			frame{"runtime/trace_test.TestTraceSymbolize", 107},
			frame{"testing.tRunner", 0},
		}},
		eventDesc{trace.EvGoCreate, []frame{
			frame{"runtime/trace_test.TestTraceSymbolize", 39},
			frame{"testing.tRunner", 0},
		}},
		eventDesc{trace.EvGoStop, []frame{
			frame{"runtime.block", 0},
			frame{"runtime/trace_test.TestTraceSymbolize.func1", 38},
		}},
		eventDesc{trace.EvGoStop, []frame{
			frame{"runtime.chansend1", 0},
			frame{"runtime/trace_test.TestTraceSymbolize.func2", 42},
		}},
		eventDesc{trace.EvGoStop, []frame{
			frame{"runtime.chanrecv1", 0},
			frame{"runtime/trace_test.TestTraceSymbolize.func3", 46},
		}},
		eventDesc{trace.EvGoBlockRecv, []frame{
			frame{"runtime.chanrecv1", 0},
			frame{"runtime/trace_test.TestTraceSymbolize.func4", 50},
		}},
		eventDesc{trace.EvGoUnblock, []frame{
			frame{"runtime.chansend1", 0},
			frame{"runtime/trace_test.TestTraceSymbolize", 109},
			frame{"testing.tRunner", 0},
		}},
		eventDesc{trace.EvGoBlockSend, []frame{
			frame{"runtime.chansend1", 0},
			frame{"runtime/trace_test.TestTraceSymbolize.func5", 54},
		}},
		eventDesc{trace.EvGoUnblock, []frame{
			frame{"runtime.chanrecv1", 0},
			frame{"runtime/trace_test.TestTraceSymbolize", 110},
			frame{"testing.tRunner", 0},
		}},
		eventDesc{trace.EvGoBlockSelect, []frame{
			frame{"runtime.selectgo", 0},
			frame{"runtime/trace_test.TestTraceSymbolize.func6", 59},
		}},
		eventDesc{trace.EvGoUnblock, []frame{
			frame{"runtime.selectgo", 0},
			frame{"runtime/trace_test.TestTraceSymbolize", 111},
			frame{"testing.tRunner", 0},
		}},
		eventDesc{trace.EvGoBlockSync, []frame{
			frame{"sync.(*Mutex).Lock", 0},
			frame{"runtime/trace_test.TestTraceSymbolize.func7", 67},
		}},
		eventDesc{trace.EvGoUnblock, []frame{
			frame{"sync.(*Mutex).Unlock", 0},
			frame{"runtime/trace_test.TestTraceSymbolize", 115},
			frame{"testing.tRunner", 0},
		}},
		eventDesc{trace.EvGoBlockSync, []frame{
			frame{"sync.(*WaitGroup).Wait", 0},
			frame{"runtime/trace_test.TestTraceSymbolize.func8", 73},
		}},
		eventDesc{trace.EvGoUnblock, []frame{
			frame{"sync.(*WaitGroup).Add", 0},
			frame{"sync.(*WaitGroup).Done", 0},
			frame{"runtime/trace_test.TestTraceSymbolize", 116},
			frame{"testing.tRunner", 0},
		}},
		eventDesc{trace.EvGoBlockCond, []frame{
			frame{"sync.(*Cond).Wait", 0},
			frame{"runtime/trace_test.TestTraceSymbolize.func9", 78},
		}},
		eventDesc{trace.EvGoUnblock, []frame{
			frame{"sync.(*Cond).Signal", 0},
			frame{"runtime/trace_test.TestTraceSymbolize", 117},
			frame{"testing.tRunner", 0},
		}},
		eventDesc{trace.EvGoSleep, []frame{
			frame{"time.Sleep", 0},
			frame{"runtime/trace_test.TestTraceSymbolize", 108},
			frame{"testing.tRunner", 0},
		}},
	}
	// Stacks for the following events are OS-dependent due to OS-specific code in net package.
	if runtime.GOOS != "windows" && runtime.GOOS != "plan9" {
		want = append(want, []eventDesc{
			eventDesc{trace.EvGoBlockNet, []frame{
				frame{"net.(*netFD).accept", 0},
				frame{"net.(*TCPListener).AcceptTCP", 0},
				frame{"net.(*TCPListener).Accept", 0},
				frame{"runtime/trace_test.TestTraceSymbolize.func10", 86},
			}},
			eventDesc{trace.EvGoSysCall, []frame{
				frame{"syscall.read", 0},
				frame{"syscall.Read", 0},
				frame{"os.(*File).read", 0},
				frame{"os.(*File).Read", 0},
				frame{"runtime/trace_test.TestTraceSymbolize.func11", 101},
			}},
		}...)
	}
	matched := make([]bool, len(want))
	for _, ev := range events {
	wantLoop:
		for i, w := range want {
			if matched[i] || w.Type != ev.Type || len(w.Stk) != len(ev.Stk) {
				continue
			}

			for fi, f := range ev.Stk {
				wf := w.Stk[fi]
				if wf.Fn != f.Fn || wf.Line != 0 && wf.Line != f.Line {
					continue wantLoop
				}
			}
			matched[i] = true
		}
	}
	for i, m := range matched {
		if m {
			continue
		}
		w := want[i]
		t.Errorf("did not match event %v at %v:%v", trace.EventDescriptions[w.Type].Name, w.Stk[0].Fn, w.Stk[0].Line)
		t.Errorf("seen the following events of this type:")
		for _, ev := range events {
			if ev.Type != w.Type {
				continue
			}
			for _, f := range ev.Stk {
				t.Logf("  %v:%v", f.Fn, f.Line)
			}
			t.Logf("---")
		}
	}
}
Пример #26
0
func TestBSSHasZeros(t *testing.T) {
	testenv.MustHaveExec(t)

	if runtime.GOOS != "windows" {
		t.Skip("skipping windows only test")
	}
	gccpath, err := exec.LookPath("gcc")
	if err != nil {
		t.Skip("skipping test: gcc is missing")
	}

	tmpdir, err := ioutil.TempDir("", "TestBSSHasZeros")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tmpdir)

	srcpath := filepath.Join(tmpdir, "a.c")
	src := `
#include <stdio.h>

int zero = 0;

int
main(void)
{
	printf("%d\n", zero);
	return 0;
}
`
	err = ioutil.WriteFile(srcpath, []byte(src), 0644)
	if err != nil {
		t.Fatal(err)
	}

	objpath := filepath.Join(tmpdir, "a.obj")
	cmd := exec.Command(gccpath, "-c", srcpath, "-o", objpath)
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("failed to build object file: %v - %v", err, string(out))
	}

	f, err := Open(objpath)
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	var bss *Section
	for _, sect := range f.Sections {
		if sect.Name == ".bss" {
			bss = sect
			break
		}
	}
	if bss == nil {
		t.Fatal("could not find .bss section")
	}
	data, err := bss.Data()
	if err != nil {
		t.Fatal(err)
	}
	if len(data) == 0 {
		t.Fatalf("%s file .bss section cannot be empty", objpath)
	}
	for _, b := range data {
		if b != 0 {
			t.Fatalf(".bss section has non zero bytes: %v", data)
		}
	}
}
Пример #27
0
// TestPassFD tests passing a file descriptor over a Unix socket.
//
// This test involved both a parent and child process. The parent
// process is invoked as a normal test, with "go test", which then
// runs the child process by running the current test binary with args
// "-test.run=^TestPassFD$" and an environment variable used to signal
// that the test should become the child process instead.
func TestPassFD(t *testing.T) {
	switch runtime.GOOS {
	case "dragonfly":
		// TODO(jsing): Figure out why sendmsg is returning EINVAL.
		t.Skip("skipping test on dragonfly")
	case "solaris":
		// TODO(aram): Figure out why ReadMsgUnix is returning empty message.
		t.Skip("skipping test on solaris, see issue 7402")
	}

	testenv.MustHaveExec(t)

	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
		passFDChild()
		return
	}

	tempDir, err := ioutil.TempDir("", "TestPassFD")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tempDir)

	fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0)
	if err != nil {
		t.Fatalf("Socketpair: %v", err)
	}
	defer syscall.Close(fds[0])
	defer syscall.Close(fds[1])
	writeFile := os.NewFile(uintptr(fds[0]), "child-writes")
	readFile := os.NewFile(uintptr(fds[1]), "parent-reads")
	defer writeFile.Close()
	defer readFile.Close()

	cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", tempDir)
	cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
	cmd.ExtraFiles = []*os.File{writeFile}

	out, err := cmd.CombinedOutput()
	if len(out) > 0 || err != nil {
		t.Fatalf("child process: %q, %v", out, err)
	}

	c, err := net.FileConn(readFile)
	if err != nil {
		t.Fatalf("FileConn: %v", err)
	}
	defer c.Close()

	uc, ok := c.(*net.UnixConn)
	if !ok {
		t.Fatalf("unexpected FileConn type; expected UnixConn, got %T", c)
	}

	buf := make([]byte, 32) // expect 1 byte
	oob := make([]byte, 32) // expect 24 bytes
	closeUnix := time.AfterFunc(5*time.Second, func() {
		t.Logf("timeout reading from unix socket")
		uc.Close()
	})
	_, oobn, _, _, err := uc.ReadMsgUnix(buf, oob)
	closeUnix.Stop()

	scms, err := syscall.ParseSocketControlMessage(oob[:oobn])
	if err != nil {
		t.Fatalf("ParseSocketControlMessage: %v", err)
	}
	if len(scms) != 1 {
		t.Fatalf("expected 1 SocketControlMessage; got scms = %#v", scms)
	}
	scm := scms[0]
	gotFds, err := syscall.ParseUnixRights(&scm)
	if err != nil {
		t.Fatalf("syscall.ParseUnixRights: %v", err)
	}
	if len(gotFds) != 1 {
		t.Fatalf("wanted 1 fd; got %#v", gotFds)
	}

	f := os.NewFile(uintptr(gotFds[0]), "fd-from-child")
	defer f.Close()

	got, err := ioutil.ReadAll(f)
	want := "Hello from child process!\n"
	if string(got) != want {
		t.Errorf("child process ReadAll: %q, %v; want %q", got, err, want)
	}
}