Commit 62956897 authored by Michael Matloob's avatar Michael Matloob

runtime: add definitions for SetGoroutineLabels and Do

This change defines runtime/pprof.SetGoroutineLabels and runtime/pprof.Do, which
are used to set profiler labels on goroutines. The change defines functions
in the runtime for setting and getting profile labels, and sets and unsets
profile labels when goroutines are created and deleted. The change also adds
the package runtime/internal/proflabel, which defines the structure the runtime
uses to store profile labels.

Change-Id: I747a4400141f89b6e8160dab6aa94ca9f0d4c94d
Reviewed-on: https://go-review.googlesource.com/34198
Run-TryBot: Michael Matloob <matloob@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarRuss Cox <rsc@golang.org>
Reviewed-on: https://go-review.googlesource.com/35010
parent bc548d71
This diff is collapsed.
......@@ -21,6 +21,14 @@ type LabelSet struct {
// labelContextKey is the type of contextKeys used for profiler labels.
type labelContextKey struct{}
func labelValue(ctx context.Context) labelMap {
labels, _ := ctx.Value(labelContextKey{}).(*labelMap)
if labels == nil {
return labelMap(nil)
}
return *labels
}
// labelMap is the representation of the label set held in the context type.
// This is an initial implementation, but it will be replaced with something
// that admits incremental immutable modification more efficiently.
......@@ -30,7 +38,7 @@ type labelMap map[string]string
// A label overwrites a prior label with the same key.
func WithLabels(ctx context.Context, labels LabelSet) context.Context {
childLabels := make(labelMap)
parentLabels, _ := ctx.Value(labelContextKey{}).(labelMap)
parentLabels := labelValue(ctx)
// TODO(matloob): replace the map implementation with something
// more efficient so creating a child context WithLabels doesn't need
// to clone the map.
......@@ -40,7 +48,7 @@ func WithLabels(ctx context.Context, labels LabelSet) context.Context {
for _, label := range labels.list {
childLabels[label.key] = label.value
}
return context.WithValue(ctx, labelContextKey{}, childLabels)
return context.WithValue(ctx, labelContextKey{}, &childLabels)
}
// Labels takes an even number of strings representing key-value pairs
......@@ -60,7 +68,7 @@ func Labels(args ...string) LabelSet {
// Label returns the value of the label with the given key on ctx, and a boolean indicating
// whether that label exists.
func Label(ctx context.Context, key string) (string, bool) {
ctxLabels, _ := ctx.Value(labelContextKey{}).(labelMap)
ctxLabels := labelValue(ctx)
v, ok := ctxLabels[key]
return v, ok
}
......@@ -68,7 +76,7 @@ func Label(ctx context.Context, key string) (string, bool) {
// ForLabels invokes f with each label set on the context.
// The function f should return true to continue iteration or false to stop iteration early.
func ForLabels(ctx context.Context, f func(key, value string) bool) {
ctxLabels, _ := ctx.Value(labelContextKey{}).(labelMap)
ctxLabels := labelValue(ctx)
for k, v := range ctxLabels {
if !f(k, v) {
break
......
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pprof
import (
"context"
"unsafe"
)
// runtime_setProfLabel is defined in runtime/proflabel.go.
func runtime_setProfLabel(labels unsafe.Pointer)
// runtime_getProfLabel is defined in runtime/proflabel.go.
func runtime_getProfLabel() unsafe.Pointer
// SetGoroutineLabels sets the current goroutine's labels to match ctx.
// This is a lower-level API than Do, which should be used instead when possible.
func SetGoroutineLabels(ctx context.Context) {
ctxLabels, _ := ctx.Value(labelContextKey{}).(*labelMap)
runtime_setProfLabel(unsafe.Pointer(ctxLabels))
}
// Do calls f with a copy of the parent context with the
// given labels added to the parent's label map.
// Each key/value pair in labels is inserted into the label map in the
// order provided, overriding any previous value for the same key.
// The augmented label map will be set for the duration of the call to f
// and restored once f returns.
func Do(ctx context.Context, labels LabelSet, f func(context.Context)) {
defer SetGoroutineLabels(ctx)
ctx = WithLabels(ctx, labels)
SetGoroutineLabels(ctx)
f(ctx)
}
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pprof
import (
"context"
"fmt"
"reflect"
"testing"
)
func TestSetGoroutineLabels(t *testing.T) {
sync := make(chan struct{})
wantLabels := map[string]string{}
if gotLabels := getProfLabel(); !reflect.DeepEqual(gotLabels, wantLabels) {
t.Errorf("Expected parent goroutine's profile labels to be empty before test, got %v", gotLabels)
}
go func() {
if gotLabels := getProfLabel(); !reflect.DeepEqual(gotLabels, wantLabels) {
t.Errorf("Expected child goroutine's profile labels to be empty before test, got %v", gotLabels)
}
sync <- struct{}{}
}()
<-sync
wantLabels = map[string]string{"key": "value"}
ctx := WithLabels(context.Background(), Labels("key", "value"))
SetGoroutineLabels(ctx)
if gotLabels := getProfLabel(); !reflect.DeepEqual(gotLabels, wantLabels) {
t.Errorf("parent goroutine's profile labels: got %v, want %v", gotLabels, wantLabels)
}
go func() {
if gotLabels := getProfLabel(); !reflect.DeepEqual(gotLabels, wantLabels) {
t.Errorf("child goroutine's profile labels: got %v, want %v", gotLabels, wantLabels)
}
sync <- struct{}{}
}()
<-sync
wantLabels = map[string]string{}
ctx = context.Background()
SetGoroutineLabels(ctx)
if gotLabels := getProfLabel(); !reflect.DeepEqual(gotLabels, wantLabels) {
t.Errorf("Expected parent goroutine's profile labels to be empty, got %v", gotLabels)
}
go func() {
if gotLabels := getProfLabel(); !reflect.DeepEqual(gotLabels, wantLabels) {
t.Errorf("Expected child goroutine's profile labels to be empty, got %v", gotLabels)
}
sync <- struct{}{}
}()
<-sync
}
func TestDo(t *testing.T) {
wantLabels := map[string]string{}
if gotLabels := getProfLabel(); !reflect.DeepEqual(gotLabels, wantLabels) {
t.Errorf("Expected parent goroutine's profile labels to be empty before Do, got %v", gotLabels)
}
Do(context.Background(), Labels("key1", "value1", "key2", "value2"), func(ctx context.Context) {
wantLabels := map[string]string{"key1": "value1", "key2": "value2"}
if gotLabels := getProfLabel(); !reflect.DeepEqual(gotLabels, wantLabels) {
t.Errorf("parent goroutine's profile labels: got %v, want %v", gotLabels, wantLabels)
}
sync := make(chan struct{})
go func() {
wantLabels := map[string]string{"key1": "value1", "key2": "value2"}
if gotLabels := getProfLabel(); !reflect.DeepEqual(gotLabels, wantLabels) {
t.Errorf("child goroutine's profile labels: got %v, want %v", gotLabels, wantLabels)
}
sync <- struct{}{}
}()
<-sync
})
wantLabels = map[string]string{}
if gotLabels := getProfLabel(); !reflect.DeepEqual(gotLabels, wantLabels) {
fmt.Printf("%#v", gotLabels)
fmt.Printf("%#v", wantLabels)
t.Errorf("Expected parent goroutine's profile labels to be empty after Do, got %v", gotLabels)
}
}
func getProfLabel() map[string]string {
l := (*labelMap)(runtime_getProfLabel())
if l == nil {
return map[string]string{}
}
return *l
}
......@@ -2343,6 +2343,7 @@ func goexit0(gp *g) {
gp.writebuf = nil
gp.waitreason = ""
gp.param = nil
gp.labels = nil
// Note that gp's stack scan is now "valid" because it has no
// stack. We could dequeueRescan, but that takes a lock and
......@@ -2920,6 +2921,9 @@ func newproc1(fn *funcval, argp *uint8, narg int32, nret int32, callerpc uintptr
gostartcallfn(&newg.sched, fn)
newg.gopc = callerpc
newg.startpc = fn.fn
if _g_.m.curg != nil {
newg.labels = _g_.m.curg.labels
}
if isSystemGoroutine(newg) {
atomic.Xadd(&sched.ngsys, +1)
}
......
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
import "unsafe"
//go:linkname runtime_setProfLabel runtime/pprof.runtime_setProfLabel
func runtime_setProfLabel(labels unsafe.Pointer) {
getg().labels = labels
}
//go:linkname runtime_getProfLabel runtime/pprof.runtime_getProfLabel
func runtime_getProfLabel() unsafe.Pointer {
return getg().labels
}
......@@ -381,6 +381,8 @@ type g struct {
waiting *sudog // sudog structures this g is waiting on (that have a valid elem ptr); in lock order
cgoCtxt []uintptr // cgo traceback context
labels unsafe.Pointer // profiler labels
// Per-G GC state
// gcRescan is this G's index in work.rescan.list. If this is
......
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