From c1ac70ff9206191d3e9daf5cf9f9de193f8dd9a8 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 20 Apr 2017 16:10:59 -0400 Subject: [PATCH] runtime/pprof: use symbol information already in profile in tests Currently the pprof tests re-symbolize PCs in profiles, and do so in a way that can't handle inlining. Proto profiles already contain full symbol information, so this modifies the tests to use the symbol information already present in the profile. Change-Id: I63cd491de7197080fd158b1e4f782630f1bbbb56 Reviewed-on: https://go-review.googlesource.com/41255 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Michael Matloob --- src/runtime/pprof/pprof_test.go | 73 +++++++++++++++++---------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/src/runtime/pprof/pprof_test.go b/src/runtime/pprof/pprof_test.go index 5d8da0eace..fd942de567 100644 --- a/src/runtime/pprof/pprof_test.go +++ b/src/runtime/pprof/pprof_test.go @@ -11,6 +11,7 @@ import ( "context" "fmt" "internal/testenv" + "io" "math/big" "os" "os/exec" @@ -86,18 +87,14 @@ func TestCPUProfileMultithreaded(t *testing.T) { }) } -func parseProfile(t *testing.T, valBytes []byte, f func(uintptr, []uintptr, map[string][]string)) { +func parseProfile(t *testing.T, valBytes []byte, f func(uintptr, []*profile.Location, map[string][]string)) { p, err := profile.Parse(bytes.NewReader(valBytes)) if err != nil { t.Fatal(err) } for _, sample := range p.Sample { count := uintptr(sample.Value[0]) - stk := make([]uintptr, len(sample.Location)) - for i := range sample.Location { - stk[i] = uintptr(sample.Location[i].Address) - } - f(count, stk, sample.Label) + f(count, sample.Location, sample.Label) } } @@ -181,26 +178,23 @@ func profileOk(t *testing.T, need []string, prof bytes.Buffer, duration time.Dur have := make([]uintptr, len(need)) var samples uintptr var buf bytes.Buffer - parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr, labels map[string][]string) { + parseProfile(t, prof.Bytes(), func(count uintptr, stk []*profile.Location, labels map[string][]string) { fmt.Fprintf(&buf, "%d:", count) + fprintStack(&buf, stk) samples += count - for _, pc := range stk { - fmt.Fprintf(&buf, " %#x", pc) - f := runtime.FuncForPC(pc) - if f == nil { - continue + for i, name := range need { + if semi := strings.Index(name, ";"); semi > -1 { + kv := strings.SplitN(name[semi+1:], "=", 2) + if len(kv) != 2 || !contains(labels[kv[0]], kv[1]) { + continue + } + name = name[:semi] } - fmt.Fprintf(&buf, "(%s)", f.Name()) - for i, name := range need { - if semi := strings.Index(name, ";"); semi > -1 { - kv := strings.SplitN(name[semi+1:], "=", 2) - if len(kv) != 2 || !contains(labels[kv[0]], kv[1]) { - continue + for _, loc := range stk { + for _, line := range loc.Line { + if strings.Contains(line.Function.Name, name) { + have[i] += count } - name = name[:semi] - } - if strings.Contains(f.Name(), name) { - have[i] += count } } } @@ -313,36 +307,43 @@ func TestGoroutineSwitch(t *testing.T) { // Read profile to look for entries for runtime.gogo with an attempt at a traceback. // The special entry - parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr, _ map[string][]string) { + parseProfile(t, prof.Bytes(), func(count uintptr, stk []*profile.Location, _ map[string][]string) { // An entry with two frames with 'System' in its top frame // exists to record a PC without a traceback. Those are okay. if len(stk) == 2 { - f := runtime.FuncForPC(stk[1]) - if f != nil && (f.Name() == "runtime._System" || f.Name() == "runtime._ExternalCode" || f.Name() == "runtime._GC") { + name := stk[1].Line[0].Function.Name + if name == "runtime._System" || name == "runtime._ExternalCode" || name == "runtime._GC" { return } } // Otherwise, should not see runtime.gogo. // The place we'd see it would be the inner most frame. - f := runtime.FuncForPC(stk[0]) - if f != nil && f.Name() == "runtime.gogo" { + name := stk[0].Line[0].Function.Name + if name == "runtime.gogo" { var buf bytes.Buffer - for _, pc := range stk { - f := runtime.FuncForPC(pc) - if f == nil { - fmt.Fprintf(&buf, "%#x ?:0\n", pc) - } else { - file, line := f.FileLine(pc) - fmt.Fprintf(&buf, "%#x %s:%d\n", pc, file, line) - } - } + fprintStack(&buf, stk) t.Fatalf("found profile entry for runtime.gogo:\n%s", buf.String()) } }) } } +func fprintStack(w io.Writer, stk []*profile.Location) { + for _, loc := range stk { + fmt.Fprintf(w, " %#x", loc.Address) + fmt.Fprintf(w, " (") + for i, line := range loc.Line { + if i > 0 { + fmt.Fprintf(w, " ") + } + fmt.Fprintf(w, "%s:%d", line.Function.Name, line.Line) + } + fmt.Fprintf(w, ")") + } + fmt.Fprintf(w, "\n") +} + // Test that profiling of division operations is okay, especially on ARM. See issue 6681. func TestMathBigDivide(t *testing.T) { testCPUProfile(t, nil, func(duration time.Duration) { -- 2.48.1