2
2
3
3
Author: Michael Matloob
4
4
5
- Last updated: 18 October 2016
5
+ Last updated: 15 May 2017 (to reflect actual implementation)
6
6
7
7
Discussion at https://golang.org/issue/17280 .
8
8
@@ -39,74 +39,88 @@ This change allows users to annotate profiles with that information for more
39
39
fine-grained profiling.
40
40
41
41
It is natural to use ` context.Context ` types to store this information, because
42
- their purpose is to hold context-dependent data.
43
- So we've added the ` context.DoWithLabels ` function which is the intended
44
- mechanism for users to set and unset profiler labels.
42
+ their purpose is to hold context-dependent data. So the ` runtime/pprof ` package
43
+ API adds labels to and changes labels on opaque ` context.Context ` values.
45
44
46
45
Supporting profiler labels necessarily changes the runtime package, because
47
46
that's where profiling is implemented.
48
- The runtime package will expose the low-level ` SetProfilerLabels ` function
49
- primarily for internal use by the context package.
50
- Like the other low-level profiling functions in the runtime, ordinary programs
51
- are not expected to call this API directly, but to use the high-level
52
- ` context.DoWithLabels ` API.
47
+ The ` runtime ` package will expose internal hooks to package ` runtime/pprof ` which
48
+ it uses to implement its ` Context ` -based API.
53
49
54
50
One goal of the design is to avoid creating a mechanism that could be used to
55
51
implement goroutine-local storage.
56
52
That's why it's possible to set profile labels but not retrieve them.
57
53
58
54
59
- ## Proposed API
60
-
61
- In this proposal, the following function will be added to the
62
- [ context] ( golang.org/pkg/context ) package.
63
-
64
- package context
65
-
66
- // DoWithProfileLabels calls f with a copy of the parent context with the
67
- // given labels added to the parent's label map.
68
- // Labels should be a slice of key-value pairs.
69
- // Labels are added to the label map in the order provided and override
70
- // any previous label with the same key.
71
- // The combined label map will be set for the duration of the call to f
72
- // and restored once f returns.
73
- func DoWithProfileLabels(parent Context, labels [][2]string, f func(ctx Context))
74
-
75
- // ProfileLabels returns a new slice containing the profile labels
76
- // on the context.
77
- func ProfileLabels(ctx Context) [][2]string
55
+ ## API
78
56
79
57
The following types and functions will be added to the
80
- [ runtime] ( golang.org/pkg/runtime ) package.
81
- They exist to support the implementation of ` context.DoWithLabels ` .
82
- As such, they are low level functions that should almost never be used outside
83
- the standard library.
58
+ [ runtime/pprof] ( golang.org/pkg/runtime/pprof ) package.
84
59
85
- package runtime
60
+ package pprof
86
61
87
- // ProfileLabels is an immutable map of profiler labels. A nil
88
- // *ProfileLabels is an empty map of labels.
89
- type ProfileLabels struct { /* runtime-internal unexported fields */ }
62
+ // SetGoroutineLabels sets the current goroutine's labels to match ctx.
63
+ // This is a lower-level API than Do, which should be used instead when possible.
64
+ func SetGoroutineLabels(ctx context.Context) {
65
+ ctxLabels, _ := ctx.Value(labelContextKey{}).(*labelMap)
66
+ runtime_setProfLabel(unsafe.Pointer(ctxLabels))
67
+ }
90
68
91
- // SetProfileLabels associates the specified profile
92
- // labels with the current goroutine.
93
- // SetProfileLabels returns the ProfileLabels currently set on
94
- // the current goroutine.
95
- func SetProfileLabels(labels *ProfileLabels) *ProfileLabels
96
-
97
- // WithLabels returns a new ProfileLabels with the given labels added.
69
+ // Do calls f with a copy of the parent context with the
70
+ // given labels added to the parent's label map.
71
+ // Each key/value pair in labels is inserted into the label map in the
72
+ // order provided, overriding any previous value for the same key.
73
+ // The augmented label map will be set for the duration of the call to f
74
+ // and restored once f returns.
75
+ func Do(ctx context.Context, labels LabelSet, f func(context.Context)) {
76
+ defer SetGoroutineLabels(ctx)
77
+ ctx = WithLabels(ctx, labels)
78
+ SetGoroutineLabels(ctx)
79
+ f(ctx)
80
+ }
81
+
82
+ // LabelSet is a set of labels.
83
+ type LabelSet struct {
84
+ list []label
85
+ }
86
+
87
+ // Labels takes an even number of strings representing key-value pairs
88
+ // and makes a LabelList containing them.
98
89
// A label overwrites a prior label with the same key.
99
- func (l *ProfileLabels) WithLabels(labels ...[2]string) *ProfileLabels
100
-
101
- // Labels returns a new slice containing the labels in the ProfileLabels.
102
- // Labels panics if called on a ProfileLabels returned by SetProfileLabels
103
- // or derived from one by WithLabels.
104
- func (l *ProfileLabels) Labels() [][2]string
90
+ func Labels(args ...string) LabelSet {
91
+ if len(args)%2 != 0 {
92
+ panic("uneven number of arguments to pprof.Labels")
93
+ }
94
+ labels := LabelSet{}
95
+ for i := 0; i+1 < len(args); i += 2 {
96
+ labels.list = append(labels.list, label{key: args[i], value: args[i+1]})
97
+ }
98
+ return labels
99
+ }
100
+
101
+ // Label returns the value of the label with the given key on ctx, and a boolean indicating
102
+ // whether that label exists.
103
+ func Label(ctx context.Context, key string) (string, bool) {
104
+ ctxLabels := labelValue(ctx)
105
+ v, ok := ctxLabels[key]
106
+ return v, ok
107
+ }
108
+
109
+ // ForLabels invokes f with each label set on the context.
110
+ // The function f should return true to continue iteration or false to stop iteration early.
111
+ func ForLabels(ctx context.Context, f func(key, value string) bool) {
112
+ ctxLabels := labelValue(ctx)
113
+ for k, v := range ctxLabels {
114
+ if !f(k, v) {
115
+ break
116
+ }
117
+ }
118
+ }
105
119
106
120
### ` Context ` changes
107
121
108
- Each ` Context ` has a set of profiler labels associated with it.
109
- ` DoWithLabels ` calls ` f ` with a new context whose labels map is
122
+ Each ` Context ` may have a set of profiler labels associated with it.
123
+ ` Do ` calls ` f ` with a new context whose labels map is
110
124
the the parent context's labels map with the additional label arguments added.
111
125
Consider the tree of function calls during an execution of the program,
112
126
treating concurrent and deferred calls like any other. The labels of a
@@ -119,10 +133,17 @@ sample records the labels of the currently executing function.
119
133
The profiler will annotate all profile samples of each goroutine by the set of
120
134
labels associated with that goroutine.
121
135
122
- The associated set of labels will also be replaced if the user calls ` SetProfileLabels ` with another
123
- ` ProfileLabels ` value.
136
+ Two hooks in the runtime, ` func runtime_setProfLabel(labels unsafe.Pointer) ` and
137
+ ` func runtime_getProfLabel() unsafe.Pointer ` are linknamed
138
+ into ` runtime/pprof ` and are used for setting and getting profile labels from the
139
+ current goroutine. These functions are only accessible from ` runtime/pprof ` , which
140
+ prevents them from being misused to implement a Goroutine-local storage facility.
141
+ The profile label implementation structure is left opaque to the runtime.
142
+
143
+ ` runtime.CPUProfile ` is deprecated. ` runtime_pprof_readProfile ` ,
144
+ another runtime function linknamed into ` runtime/pprof ` , is added as a way for ` runtime/pprof ` to retrieve the raw label-annotated profile data.
124
145
125
- New goroutines inherit the ProfileLabels value set on their creator.
146
+ New goroutines inherit the labels set on their creator.
126
147
127
148
## Compatibility
128
149
@@ -133,13 +154,11 @@ them.
133
154
134
155
## Implementation
135
156
136
- Because ` context ` and ` runtime ` have compatible notions of profile labels,
137
- ` context.Context ` can simply store a ` *runtime.ProfileLabels ` . Internally,
138
- ` context.Context ` can extend its label set directly using ` runtime.WithLabels ` ,
139
- which means there's no performance penalty to using the higher-level context
140
- API.
157
+ ` context.Context ` will have an internal label set representation associated with it.
158
+ This leaves the option open to change the implementation in the future to improve
159
+ the performance characteristics of using profiler labels.
141
160
142
- Initially, ` runtime.ProfileLabels ` may be implemented as a simple
161
+ The initial implementation of the label set is a
143
162
` map[string]string ` that is copied when new labels are added. However, the
144
163
specification permits more sophisticated implementations that scale to large
145
164
numbers of label changes such as persistent set structures or diff arrays. This
@@ -150,15 +169,15 @@ This change requires the profile signal handler to interact with pointers, which
150
169
means it has to interact with the garbage collector.
151
170
There are two complications to this:
152
171
153
- 1 . This requires the profile signal handler to save a ` *ProfileLabels ` in the
172
+ 1 . This requires the profile signal handler to save the label set structure in the
154
173
CPU profile structure, which is allocated off-heap.
155
174
Addressing this will require either adding the CPU profile structure as a new GC
156
175
root, or allocating the CPU profile structure in the garbage-collected heap.
157
176
158
- 2 . Normally, writing the ` *ProfileLabels ` to the CPU profile structure would
177
+ 2 . Normally, writing the label set structure to the CPU profile structure would
159
178
require a write barrier, but write barriers are disallowed in a signal handler.
160
179
This can be addressed by treating the CPU profile structure similar to stacks,
161
180
which also do not have write barriers.
162
181
This could mean a STW re-scan of the CPU profile structure, or shading the old
163
- ` *ProfileLabels ` when ` SetProfileLabels ` replaces it.
182
+ label set structure when ` SetGoroutineLabels ` replaces it.
164
183
0 commit comments