aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/pprof/pprof_test.go
blob: 5c7b19d926ba3001e32679bde1263b25acbf65f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"internal/testenv"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

var tmp, pprofExe string // populated by buildPprof

func TestMain(m *testing.M) {
	if !testenv.HasGoBuild() {
		return
	}

	var exitcode int
	if err := buildPprof(); err == nil {
		exitcode = m.Run()
	} else {
		fmt.Println(err)
		exitcode = 1
	}
	os.RemoveAll(tmp)
	os.Exit(exitcode)
}

func buildPprof() error {
	var err error
	tmp, err = os.MkdirTemp("", "TestPprof")
	if err != nil {
		return fmt.Errorf("TempDir failed: %v", err)
	}

	pprofExe = filepath.Join(tmp, "testpprof.exe")
	gotool, err := testenv.GoTool()
	if err != nil {
		return err
	}
	out, err := exec.Command(gotool, "build", "-o", pprofExe, "cmd/pprof").CombinedOutput()
	if err != nil {
		os.RemoveAll(tmp)
		return fmt.Errorf("go build -o %v cmd/pprof: %v\n%s", pprofExe, err, string(out))
	}

	return nil
}

// See also runtime/pprof.cpuProfilingBroken.
func mustHaveCPUProfiling(t *testing.T) {
	switch runtime.GOOS {
	case "plan9":
		t.Skipf("skipping on %s, unimplemented", runtime.GOOS)
	case "aix":
		t.Skipf("skipping on %s, issue 45170", runtime.GOOS)
	case "ios", "dragonfly", "netbsd", "illumos", "solaris":
		t.Skipf("skipping on %s, issue 13841", runtime.GOOS)
	case "openbsd":
		if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
			t.Skipf("skipping on %s/%s, issue 13841", runtime.GOOS, runtime.GOARCH)
		}
	}
}

func mustHaveDisasm(t *testing.T) {
	switch runtime.GOARCH {
	case "loong64":
		t.Skipf("skipping on %s.", runtime.GOARCH)
	case "mips", "mipsle", "mips64", "mips64le":
		t.Skipf("skipping on %s, issue 12559", runtime.GOARCH)
	case "riscv64":
		t.Skipf("skipping on %s, issue 36738", runtime.GOARCH)
	case "s390x":
		t.Skipf("skipping on %s, issue 15255", runtime.GOARCH)
	}

	// Skip PIE platforms, pprof can't disassemble PIE.
	if runtime.GOOS == "windows" {
		t.Skipf("skipping on %s, issue 46639", runtime.GOOS)
	}
	if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
		t.Skipf("skipping on %s/%s, issue 46639", runtime.GOOS, runtime.GOARCH)
	}
}

// TestDisasm verifies that cmd/pprof can successfully disassemble functions.
//
// This is a regression test for issue 46636.
func TestDisasm(t *testing.T) {
	mustHaveCPUProfiling(t)
	mustHaveDisasm(t)
	testenv.MustHaveGoBuild(t)

	if runtime.GOARCH == "arm64" {
		// Fixed at tip (issue 56574). Skip for Go 1.19 release branch.
		testenv.SkipFlaky(t, 60637)
	}

	tmpdir := t.TempDir()
	cpuExe := filepath.Join(tmpdir, "cpu.exe")
	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", cpuExe, "cpu.go")
	cmd.Dir = "testdata/"
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("build failed: %v\n%s", err, out)
	}

	profile := filepath.Join(tmpdir, "cpu.pprof")
	cmd = exec.Command(cpuExe, "-output", profile)
	out, err = cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("cpu failed: %v\n%s", err, out)
	}

	cmd = exec.Command(pprofExe, "-disasm", "main.main", cpuExe, profile)
	out, err = cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("pprof failed: %v\n%s", err, out)
	}

	sout := string(out)
	want := "ROUTINE ======================== main.main"
	if !strings.Contains(sout, want) {
		t.Errorf("pprof disasm got %s want contains %q", sout, want)
	}
}