runtime,runtime/metrics: use explicit histogram boundaries

This change modifies the semantics of
runtime/metrics.Float64Histogram.Buckets to remove implicit buckets to
that extend to positive and negative infinity and instead defines all
bucket boundaries as explicitly listed.

Bucket boundaries remain the same as before except
/gc/heap/allocs-by-size:objects and /gc/heap/frees-by-size:objects no
longer have a bucket that extends to negative infinity.

This change simplifies the Float64Histogram API, making it both easier
to understand and easier to use.

Also, add a test for allocs-by-size and frees-by-size that checks them
against MemStats.

Fixes #43443.

Change-Id: I5620f15bd084562dadf288f733c4a8cace21910c
Reviewed-on: https://go-review.googlesource.com/c/go/+/281238
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
Trust: Michael Knyszek <mknyszek@google.com>
This commit is contained in:
Michael Anthony Knyszek 2021-01-06 23:05:22 +00:00 committed by Michael Knyszek
parent a9ccd2d795
commit ae97717133
4 changed files with 99 additions and 30 deletions

View file

@ -70,6 +70,34 @@ func TestReadMetrics(t *testing.T) {
checkUint64(t, name, samples[i].Value.Uint64(), mstats.BuckHashSys)
case "/memory/classes/total:bytes":
checkUint64(t, name, samples[i].Value.Uint64(), mstats.Sys)
case "/gc/heap/allocs-by-size:objects":
hist := samples[i].Value.Float64Histogram()
// Skip size class 0 in BySize, because it's always empty and not represented
// in the histogram.
for i, sc := range mstats.BySize[1:] {
if b, s := hist.Buckets[i+1], float64(sc.Size+1); b != s {
t.Errorf("bucket does not match size class: got %f, want %f", b, s)
// The rest of the checks aren't expected to work anyway.
continue
}
if c, m := hist.Counts[i], sc.Mallocs; c != m {
t.Errorf("histogram counts do not much BySize for class %d: got %d, want %d", i, c, m)
}
}
case "/gc/heap/frees-by-size:objects":
hist := samples[i].Value.Float64Histogram()
// Skip size class 0 in BySize, because it's always empty and not represented
// in the histogram.
for i, sc := range mstats.BySize[1:] {
if b, s := hist.Buckets[i+1], float64(sc.Size+1); b != s {
t.Errorf("bucket does not match size class: got %f, want %f", b, s)
// The rest of the checks aren't expected to work anyway.
continue
}
if c, f := hist.Counts[i], sc.Frees; c != f {
t.Errorf("histogram counts do not much BySize for class %d: got %d, want %d", i, c, f)
}
}
case "/gc/heap/objects:objects":
checkUint64(t, name, samples[i].Value.Uint64(), mstats.HeapObjects)
case "/gc/heap/goal:bytes":
@ -154,11 +182,11 @@ func TestReadMetricsConsistency(t *testing.T) {
if totalVirtual.got != totalVirtual.want {
t.Errorf(`"/memory/classes/total:bytes" does not match sum of /memory/classes/**: got %d, want %d`, totalVirtual.got, totalVirtual.want)
}
if objects.alloc.Counts[0] > 0 {
t.Error("found counts for objects of non-positive size in allocs-by-size")
if b, c := len(objects.alloc.Buckets), len(objects.alloc.Counts); b != c+1 {
t.Errorf("allocs-by-size has wrong bucket or counts length: %d buckets, %d counts", b, c)
}
if objects.free.Counts[0] > 0 {
t.Error("found counts for objects of non-positive size in frees-by-size")
if b, c := len(objects.free.Buckets), len(objects.free.Counts); b != c+1 {
t.Errorf("frees-by-size has wrong bucket or counts length: %d buckets, %d counts", b, c)
}
if len(objects.alloc.Buckets) != len(objects.free.Buckets) {
t.Error("allocs-by-size and frees-by-size buckets don't match in length")