1// Copyright 2021 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 5package main 6 7import ( 8 "internal/testenv" 9 "os" 10 "path/filepath" 11 "runtime" 12 "strings" 13 "sync" 14 "testing" 15) 16 17// TestMain executes the test binary as the pprof command if 18// GO_PPROFTEST_IS_PPROF is set, and runs the tests otherwise. 19func TestMain(m *testing.M) { 20 if os.Getenv("GO_PPROFTEST_IS_PPROF") != "" { 21 main() 22 os.Exit(0) 23 } 24 25 os.Setenv("GO_PPROFTEST_IS_PPROF", "1") // Set for subprocesses to inherit. 26 os.Exit(m.Run()) 27} 28 29// pprofPath returns the path to the "pprof" binary to run. 30func pprofPath(t testing.TB) string { 31 t.Helper() 32 testenv.MustHaveExec(t) 33 34 pprofPathOnce.Do(func() { 35 pprofExePath, pprofPathErr = os.Executable() 36 }) 37 if pprofPathErr != nil { 38 t.Fatal(pprofPathErr) 39 } 40 return pprofExePath 41} 42 43var ( 44 pprofPathOnce sync.Once 45 pprofExePath string 46 pprofPathErr error 47) 48 49// See also runtime/pprof.cpuProfilingBroken. 50func mustHaveCPUProfiling(t *testing.T) { 51 switch runtime.GOOS { 52 case "plan9": 53 t.Skipf("skipping on %s, unimplemented", runtime.GOOS) 54 case "aix": 55 t.Skipf("skipping on %s, issue 45170", runtime.GOOS) 56 case "ios", "dragonfly", "netbsd", "illumos", "solaris": 57 t.Skipf("skipping on %s, issue 13841", runtime.GOOS) 58 case "openbsd": 59 if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" { 60 t.Skipf("skipping on %s/%s, issue 13841", runtime.GOOS, runtime.GOARCH) 61 } 62 } 63} 64 65func mustHaveDisasm(t *testing.T) { 66 switch runtime.GOARCH { 67 case "loong64": 68 t.Skipf("skipping on %s.", runtime.GOARCH) 69 case "mips", "mipsle", "mips64", "mips64le": 70 t.Skipf("skipping on %s, issue 12559", runtime.GOARCH) 71 case "riscv64": 72 t.Skipf("skipping on %s, issue 36738", runtime.GOARCH) 73 case "s390x": 74 t.Skipf("skipping on %s, issue 15255", runtime.GOARCH) 75 } 76 77 // pprof can only disassemble PIE on some platforms. 78 // Skip the ones it can't handle yet. 79 if runtime.GOOS == "android" && runtime.GOARCH == "arm" { 80 t.Skipf("skipping on %s/%s, issue 46639", runtime.GOOS, runtime.GOARCH) 81 } 82} 83 84// TestDisasm verifies that cmd/pprof can successfully disassemble functions. 85// 86// This is a regression test for issue 46636. 87func TestDisasm(t *testing.T) { 88 mustHaveCPUProfiling(t) 89 mustHaveDisasm(t) 90 testenv.MustHaveGoBuild(t) 91 92 tmpdir := t.TempDir() 93 cpuExe := filepath.Join(tmpdir, "cpu.exe") 94 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", cpuExe, "cpu.go") 95 cmd.Dir = "testdata/" 96 out, err := cmd.CombinedOutput() 97 if err != nil { 98 t.Fatalf("build failed: %v\n%s", err, out) 99 } 100 101 profile := filepath.Join(tmpdir, "cpu.pprof") 102 cmd = testenv.Command(t, cpuExe, "-output", profile) 103 out, err = cmd.CombinedOutput() 104 if err != nil { 105 t.Fatalf("cpu failed: %v\n%s", err, out) 106 } 107 108 cmd = testenv.Command(t, pprofPath(t), "-disasm", "main.main", cpuExe, profile) 109 out, err = cmd.CombinedOutput() 110 if err != nil { 111 t.Errorf("pprof -disasm failed: %v\n%s", err, out) 112 113 // Try to print out profile content for debugging. 114 cmd = testenv.Command(t, pprofPath(t), "-raw", cpuExe, profile) 115 out, err = cmd.CombinedOutput() 116 if err != nil { 117 t.Logf("pprof -raw failed: %v\n%s", err, out) 118 } else { 119 t.Logf("profile content:\n%s", out) 120 } 121 return 122 } 123 124 sout := string(out) 125 want := "ROUTINE ======================== main.main" 126 if !strings.Contains(sout, want) { 127 t.Errorf("pprof -disasm got %s want contains %q", sout, want) 128 } 129} 130