mirror of
https://github.com/golang/go
synced 2024-11-12 03:50:21 -07:00
runtime/pprof: emit count profiles with debug=0 as proto profiles
count profiles with debug=1 retain their previous format. Also add a test check for the proto profiles since all runtime/pprof tests only look at the debug=1 profiles. Change-Id: Ibe805585b597e5d3570807115940a1dc4535c03f Reviewed-on: https://go-review.googlesource.com/33148 Run-TryBot: Michael Matloob <matloob@golang.org> Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
0bae74e8c9
commit
ccd69d0582
@ -175,7 +175,7 @@ var pkgDeps = map[string][]string{
|
||||
"regexp/syntax": {"L2"},
|
||||
"runtime/debug": {"L2", "fmt", "io/ioutil", "os", "time"},
|
||||
"runtime/pprof/internal/protopprof": {"L2", "fmt", "internal/pprof/profile", "os", "time"},
|
||||
"runtime/pprof": {"L2", "fmt", "os", "runtime/pprof/internal/protopprof", "text/tabwriter", "time"},
|
||||
"runtime/pprof": {"L2", "fmt", "internal/pprof/profile", "os", "runtime/pprof/internal/protopprof", "text/tabwriter", "time"},
|
||||
"runtime/trace": {"L0"},
|
||||
"text/tabwriter": {"L2"},
|
||||
|
||||
|
@ -73,6 +73,7 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"internal/pprof/profile"
|
||||
"io"
|
||||
"runtime"
|
||||
"runtime/pprof/internal/protopprof"
|
||||
@ -340,17 +341,8 @@ type countProfile interface {
|
||||
}
|
||||
|
||||
// printCountProfile prints a countProfile at the specified debug level.
|
||||
// The profile will be in compressed proto format unless debug is nonzero.
|
||||
func printCountProfile(w io.Writer, debug int, name string, p countProfile) error {
|
||||
b := bufio.NewWriter(w)
|
||||
var tw *tabwriter.Writer
|
||||
w = b
|
||||
if debug > 0 {
|
||||
tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
|
||||
w = tw
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s profile: total %d\n", name, p.Len())
|
||||
|
||||
// Build count of each stack.
|
||||
var buf bytes.Buffer
|
||||
key := func(stk []uintptr) string {
|
||||
@ -376,17 +368,37 @@ func printCountProfile(w io.Writer, debug int, name string, p countProfile) erro
|
||||
|
||||
sort.Sort(&keysByCount{keys, count})
|
||||
|
||||
for _, k := range keys {
|
||||
fmt.Fprintf(w, "%d %s\n", count[k], k)
|
||||
if debug > 0 {
|
||||
printStackRecord(w, p.Stack(index[k]), false)
|
||||
if debug > 0 {
|
||||
// Print debug profile in legacy format
|
||||
tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
|
||||
fmt.Fprintf(tw, "%s profile: total %d\n", name, p.Len())
|
||||
for _, k := range keys {
|
||||
fmt.Fprintf(tw, "%d %s\n", count[k], k)
|
||||
printStackRecord(tw, p.Stack(index[k]), false)
|
||||
}
|
||||
return tw.Flush()
|
||||
}
|
||||
|
||||
if tw != nil {
|
||||
tw.Flush()
|
||||
// Output profile in protobuf form.
|
||||
prof := &profile.Profile{
|
||||
PeriodType: &profile.ValueType{Type: name, Unit: "count"},
|
||||
Period: 1,
|
||||
Sample: make([]*profile.Sample, 0, len(keys)),
|
||||
SampleType: []*profile.ValueType{{Type: name, Unit: "count"}},
|
||||
}
|
||||
return b.Flush()
|
||||
for _, k := range keys {
|
||||
stk := p.Stack(index[k])
|
||||
c := count[k]
|
||||
locs := make([]*profile.Location, len(stk))
|
||||
for i, addr := range stk {
|
||||
locs[i] = &profile.Location{Address: uint64(addr) - 1}
|
||||
}
|
||||
prof.Sample = append(prof.Sample, &profile.Sample{
|
||||
Location: locs,
|
||||
Value: []int64{int64(c)},
|
||||
})
|
||||
}
|
||||
return prof.Write(w)
|
||||
}
|
||||
|
||||
// keysByCount sorts keys with higher counts first, breaking ties by key string order.
|
||||
|
@ -679,13 +679,31 @@ func TestGoroutineCounts(t *testing.T) {
|
||||
time.Sleep(10 * time.Millisecond) // let goroutines block on channel
|
||||
|
||||
var w bytes.Buffer
|
||||
Lookup("goroutine").WriteTo(&w, 1)
|
||||
goroutineProf := Lookup("goroutine")
|
||||
|
||||
// Check debug profile
|
||||
goroutineProf.WriteTo(&w, 1)
|
||||
prof := w.String()
|
||||
|
||||
if !containsInOrder(prof, "\n50 @ ", "\n40 @", "\n10 @", "\n1 @") {
|
||||
t.Errorf("expected sorted goroutine counts:\n%s", prof)
|
||||
}
|
||||
|
||||
// Check proto profile
|
||||
w.Reset()
|
||||
goroutineProf.WriteTo(&w, 0)
|
||||
p, err := profile.Parse(&w)
|
||||
if err != nil {
|
||||
t.Errorf("error parsing protobuf profile: %v", err)
|
||||
}
|
||||
if err := p.CheckValid(); err != nil {
|
||||
t.Errorf("protobuf profile is invalid: %v", err)
|
||||
}
|
||||
if !containsCounts(p, []int64{50, 40, 10, 1}) {
|
||||
t.Errorf("expected count profile to contain goroutines with counts %v, got %v",
|
||||
[]int64{50, 40, 10, 1}, p)
|
||||
}
|
||||
|
||||
close(c)
|
||||
|
||||
time.Sleep(10 * time.Millisecond) // let goroutines exit
|
||||
@ -701,3 +719,23 @@ func containsInOrder(s string, all ...string) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func containsCounts(prof *profile.Profile, counts []int64) bool {
|
||||
m := make(map[int64]int)
|
||||
for _, c := range counts {
|
||||
m[c]++
|
||||
}
|
||||
for _, s := range prof.Sample {
|
||||
// The count is the single value in the sample
|
||||
if len(s.Value) != 1 {
|
||||
return false
|
||||
}
|
||||
m[s.Value[0]]--
|
||||
}
|
||||
for _, n := range m {
|
||||
if n > 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user