mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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 <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
parent
2133d63fa8
commit
c1ac70ff92
1 changed files with 38 additions and 37 deletions
|
|
@ -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
|
||||
}
|
||||
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
|
||||
}
|
||||
name = name[:semi]
|
||||
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
|
||||
}
|
||||
if strings.Contains(f.Name(), name) {
|
||||
have[i] += count
|
||||
name = name[:semi]
|
||||
}
|
||||
for _, loc := range stk {
|
||||
for _, line := range loc.Line {
|
||||
if strings.Contains(line.Function.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) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue