]> Cypherpunks repositories - gostls13.git/commitdiff
runtime/pprof: use symbol information already in profile in tests
authorAustin Clements <austin@google.com>
Thu, 20 Apr 2017 20:10:59 +0000 (16:10 -0400)
committerAustin Clements <austin@google.com>
Fri, 28 Apr 2017 22:58:11 +0000 (22:58 +0000)
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 <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
src/runtime/pprof/pprof_test.go

index 5d8da0eace98c88c6075bcf661835ae0e13017b2..fd942de567502d6d6c3615a4eb691f93f6fb15d9 100644 (file)
@@ -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) {