Skip to content

Commit 91ad2a2

Browse files
committed
runtime/pprof: add definitions of profile label types
This change defines WithLabels, Labels, Label, and ForLabels. This is the first step of the profile labels implemention for go 1.9. Updates #17280 Change-Id: I2dfc9aae90f7a4aa1ff7080d5747f0a1f0728e75 Reviewed-on: https://go-review.googlesource.com/34198 Run-TryBot: Michael Matloob <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent 47d2a4d commit 91ad2a2

File tree

3 files changed

+160
-1
lines changed

3 files changed

+160
-1
lines changed

src/go/build/deps_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ var pkgDeps = map[string][]string{
175175
"regexp/syntax": {"L2"},
176176
"runtime/debug": {"L2", "fmt", "io/ioutil", "os", "time"},
177177
"runtime/pprof/internal/protopprof": {"L2", "fmt", "internal/pprof/profile", "os", "time"},
178-
"runtime/pprof": {"L2", "fmt", "internal/pprof/profile", "os", "runtime/pprof/internal/protopprof", "text/tabwriter", "time"},
178+
"runtime/pprof": {"L2", "context", "fmt", "internal/pprof/profile", "os", "runtime/pprof/internal/protopprof", "text/tabwriter", "time"},
179179
"runtime/trace": {"L0"},
180180
"text/tabwriter": {"L2"},
181181

src/runtime/pprof/label.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright 2016 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package pprof
6+
7+
import (
8+
"context"
9+
)
10+
11+
type label struct {
12+
key string
13+
value string
14+
}
15+
16+
// LabelSet is a set of labels.
17+
type LabelSet struct {
18+
list []label
19+
}
20+
21+
// labelContextKey is the type of contextKeys used for profiler labels.
22+
type labelContextKey struct{}
23+
24+
// labelMap is the representation of the label set held in the context type.
25+
// This is an initial implementation, but it will be replaced with something
26+
// that admits incremental immutable modification more efficiently.
27+
type labelMap map[string]string
28+
29+
// WithLabels returns a new context.Context with the given labels added.
30+
// A label overwrites a prior label with the same key.
31+
func WithLabels(ctx context.Context, labels LabelSet) context.Context {
32+
childLabels := make(labelMap)
33+
parentLabels, _ := ctx.Value(labelContextKey{}).(labelMap)
34+
// TODO(matloob): replace the map implementation with something
35+
// more efficient so creating a child context WithLabels doesn't need
36+
// to clone the map.
37+
for k, v := range parentLabels {
38+
childLabels[k] = v
39+
}
40+
for _, label := range labels.list {
41+
childLabels[label.key] = label.value
42+
}
43+
return context.WithValue(ctx, labelContextKey{}, childLabels)
44+
}
45+
46+
// Labels takes an even number of strings representing key-value pairs
47+
// and makes a LabelList containing them.
48+
// A label overwrites a prior label with the same key.
49+
func Labels(args ...string) LabelSet {
50+
if len(args)%2 != 0 {
51+
panic("uneven number of arguments to pprof.Labels")
52+
}
53+
labels := LabelSet{}
54+
for i := 0; i+1 < len(args); i += 2 {
55+
labels.list = append(labels.list, label{key: args[i], value: args[i+1]})
56+
}
57+
return labels
58+
}
59+
60+
// Label returns the value of the label with the given key on ctx, and a boolean indicating
61+
// whether that label exists.
62+
func Label(ctx context.Context, key string) (string, bool) {
63+
ctxLabels, _ := ctx.Value(labelContextKey{}).(labelMap)
64+
v, ok := ctxLabels[key]
65+
return v, ok
66+
}
67+
68+
// ForLabels invokes f with each label set on the context.
69+
// The function f should return true to continue iteration or false to stop iteration early.
70+
func ForLabels(ctx context.Context, f func(key, value string) bool) {
71+
ctxLabels, _ := ctx.Value(labelContextKey{}).(labelMap)
72+
for k, v := range ctxLabels {
73+
if !f(k, v) {
74+
break
75+
}
76+
}
77+
}

src/runtime/pprof/label_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package pprof
2+
3+
import (
4+
"context"
5+
"reflect"
6+
"sort"
7+
"testing"
8+
)
9+
10+
func labelsSorted(ctx context.Context) []label {
11+
ls := []label{}
12+
ForLabels(ctx, func(key, value string) bool {
13+
ls = append(ls, label{key, value})
14+
return true
15+
})
16+
sort.Sort(labelSorter(ls))
17+
return ls
18+
}
19+
20+
type labelSorter []label
21+
22+
func (s labelSorter) Len() int { return len(s) }
23+
func (s labelSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
24+
func (s labelSorter) Less(i, j int) bool { return s[i].key < s[j].key }
25+
26+
func TestContextLabels(t *testing.T) {
27+
// Background context starts with no lablels.
28+
ctx := context.Background()
29+
labels := labelsSorted(ctx)
30+
if len(labels) != 0 {
31+
t.Errorf("labels on background context: want [], got %v ", labels)
32+
}
33+
34+
// Add a single label.
35+
ctx = WithLabels(ctx, Labels("key", "value"))
36+
// Retreive it with Label.
37+
v, ok := Label(ctx, "key")
38+
if !ok || v != "value" {
39+
t.Errorf(`Label(ctx, "key"): got %v, %v; want "value", ok`, v, ok)
40+
}
41+
gotLabels := labelsSorted(ctx)
42+
wantLabels := []label{{"key", "value"}}
43+
if !reflect.DeepEqual(gotLabels, wantLabels) {
44+
t.Errorf("(sorted) labels on context: got %v, want %v", gotLabels, wantLabels)
45+
}
46+
47+
// Add a label with a different key.
48+
ctx = WithLabels(ctx, Labels("key2", "value2"))
49+
v, ok = Label(ctx, "key2")
50+
if !ok || v != "value2" {
51+
t.Errorf(`Label(ctx, "key2"): got %v, %v; want "value2", ok`, v, ok)
52+
}
53+
gotLabels = labelsSorted(ctx)
54+
wantLabels = []label{{"key", "value"}, {"key2", "value2"}}
55+
if !reflect.DeepEqual(gotLabels, wantLabels) {
56+
t.Errorf("(sorted) labels on context: got %v, want %v", gotLabels, wantLabels)
57+
}
58+
59+
// Add label with first key to test label replacement.
60+
ctx = WithLabels(ctx, Labels("key", "value3"))
61+
v, ok = Label(ctx, "key")
62+
if !ok || v != "value3" {
63+
t.Errorf(`Label(ctx, "key3"): got %v, %v; want "value3", ok`, v, ok)
64+
}
65+
gotLabels = labelsSorted(ctx)
66+
wantLabels = []label{{"key", "value3"}, {"key2", "value2"}}
67+
if !reflect.DeepEqual(gotLabels, wantLabels) {
68+
t.Errorf("(sorted) labels on context: got %v, want %v", gotLabels, wantLabels)
69+
}
70+
71+
// Labels called with two labels with the same key should pick the second.
72+
ctx = WithLabels(ctx, Labels("key4", "value4a", "key4", "value4b"))
73+
v, ok = Label(ctx, "key4")
74+
if !ok || v != "value4b" {
75+
t.Errorf(`Label(ctx, "key4"): got %v, %v; want "value4b", ok`, v, ok)
76+
}
77+
gotLabels = labelsSorted(ctx)
78+
wantLabels = []label{{"key", "value3"}, {"key2", "value2"}, {"key4", "value4b"}}
79+
if !reflect.DeepEqual(gotLabels, wantLabels) {
80+
t.Errorf("(sorted) labels on context: got %v, want %v", gotLabels, wantLabels)
81+
}
82+
}

0 commit comments

Comments
 (0)