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
|
// 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 (
"internal/testenv"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"testing"
)
// TestMain executes the test binary as the pprof command if
// GO_PPROFTEST_IS_PPROF is set, and runs the tests otherwise.
func TestMain(m *testing.M) {
if os.Getenv("GO_PPROFTEST_IS_PPROF") != "" {
main()
os.Exit(0)
}
os.Setenv("GO_PPROFTEST_IS_PPROF", "1") // Set for subprocesses to inherit.
os.Exit(m.Run())
}
// pprofPath returns the path to the "pprof" binary to run.
func pprofPath(t testing.TB) string {
t.Helper()
testenv.MustHaveExec(t)
pprofPathOnce.Do(func() {
pprofExePath, pprofPathErr = os.Executable()
})
if pprofPathErr != nil {
t.Fatal(pprofPathErr)
}
return pprofExePath
}
var (
pprofPathOnce sync.Once
pprofExePath string
pprofPathErr error
)
// 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)
}
// pprof can only disassemble PIE on some platforms.
// Skip the ones it can't handle yet.
if (runtime.GOOS == "darwin" && runtime.GOARCH == "arm64") ||
(runtime.GOOS == "android" && runtime.GOARCH == "arm") {
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)
tmpdir := t.TempDir()
cpuExe := filepath.Join(tmpdir, "cpu.exe")
cmd := testenv.Command(t, 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 = testenv.Command(t, cpuExe, "-output", profile)
out, err = cmd.CombinedOutput()
if err != nil {
t.Fatalf("cpu failed: %v\n%s", err, out)
}
cmd = testenv.Command(t, pprofPath(t), "-disasm", "main.main", cpuExe, profile)
out, err = cmd.CombinedOutput()
if err != nil {
t.Errorf("pprof -disasm failed: %v\n%s", err, out)
// Try to print out profile content for debugging.
cmd = testenv.Command(t, pprofPath(t), "-raw", cpuExe, profile)
out, err = cmd.CombinedOutput()
if err != nil {
t.Logf("pprof -raw failed: %v\n%s", err, out)
} else {
t.Logf("profile content:\n%s", out)
}
return
}
sout := string(out)
want := "ROUTINE ======================== main.main"
if !strings.Contains(sout, want) {
t.Errorf("pprof -disasm got %s want contains %q", sout, want)
}
}
|