Commit ccd69d05 authored by Michael Matloob's avatar Michael Matloob

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: default avatarRuss Cox <rsc@golang.org>
parent 0bae74e8
...@@ -175,7 +175,7 @@ var pkgDeps = map[string][]string{ ...@@ -175,7 +175,7 @@ var pkgDeps = map[string][]string{
"regexp/syntax": {"L2"}, "regexp/syntax": {"L2"},
"runtime/debug": {"L2", "fmt", "io/ioutil", "os", "time"}, "runtime/debug": {"L2", "fmt", "io/ioutil", "os", "time"},
"runtime/pprof/internal/protopprof": {"L2", "fmt", "internal/pprof/profile", "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"}, "runtime/trace": {"L0"},
"text/tabwriter": {"L2"}, "text/tabwriter": {"L2"},
......
...@@ -73,6 +73,7 @@ import ( ...@@ -73,6 +73,7 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"fmt" "fmt"
"internal/pprof/profile"
"io" "io"
"runtime" "runtime"
"runtime/pprof/internal/protopprof" "runtime/pprof/internal/protopprof"
...@@ -340,17 +341,8 @@ type countProfile interface { ...@@ -340,17 +341,8 @@ type countProfile interface {
} }
// printCountProfile prints a countProfile at the specified debug level. // 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 { 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. // Build count of each stack.
var buf bytes.Buffer var buf bytes.Buffer
key := func(stk []uintptr) string { key := func(stk []uintptr) string {
...@@ -376,17 +368,37 @@ func printCountProfile(w io.Writer, debug int, name string, p countProfile) erro ...@@ -376,17 +368,37 @@ func printCountProfile(w io.Writer, debug int, name string, p countProfile) erro
sort.Sort(&keysByCount{keys, count}) sort.Sort(&keysByCount{keys, count})
for _, k := range keys {
fmt.Fprintf(w, "%d %s\n", count[k], k)
if debug > 0 { if debug > 0 {
printStackRecord(w, p.Stack(index[k]), false) // 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 { // Output profile in protobuf form.
tw.Flush() 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. // keysByCount sorts keys with higher counts first, breaking ties by key string order.
......
...@@ -679,13 +679,31 @@ func TestGoroutineCounts(t *testing.T) { ...@@ -679,13 +679,31 @@ func TestGoroutineCounts(t *testing.T) {
time.Sleep(10 * time.Millisecond) // let goroutines block on channel time.Sleep(10 * time.Millisecond) // let goroutines block on channel
var w bytes.Buffer var w bytes.Buffer
Lookup("goroutine").WriteTo(&w, 1) goroutineProf := Lookup("goroutine")
// Check debug profile
goroutineProf.WriteTo(&w, 1)
prof := w.String() prof := w.String()
if !containsInOrder(prof, "\n50 @ ", "\n40 @", "\n10 @", "\n1 @") { if !containsInOrder(prof, "\n50 @ ", "\n40 @", "\n10 @", "\n1 @") {
t.Errorf("expected sorted goroutine counts:\n%s", prof) 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) close(c)
time.Sleep(10 * time.Millisecond) // let goroutines exit time.Sleep(10 * time.Millisecond) // let goroutines exit
...@@ -701,3 +719,23 @@ func containsInOrder(s string, all ...string) bool { ...@@ -701,3 +719,23 @@ func containsInOrder(s string, all ...string) bool {
} }
return true 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
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment