Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions src/runtime/pprof/label.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package pprof

// Goroutine labels, as in upstream Go. The label set lives in the
// context.Context, so nothing here needs the runtime.
//
// This is upstream's pre-1.24 form. Go 1.24 moved the label type into
// internal/runtime/pprof/label, which TinyGo does not have.

import (
"context"
"fmt"
"slices"
"strings"
)

// label is a single key/value pair in a LabelSet.
type label struct {
key string
value string
}

// LabelSet is a set of labels.
type LabelSet struct {
list []label
}

// 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.
type labelMap map[string]string

// String satisfies Stringer and returns key, value pairs in a consistent
// order.
func (l *labelMap) String() string {
if l == nil {
return ""
}
keyVals := make([]string, 0, len(*l))
for k, v := range *l {
keyVals = append(keyVals, fmt.Sprintf("%q:%q", k, v))
}
slices.Sort(keyVals)
return "{" + strings.Join(keyVals, ", ") + "}"
}

// WithLabels returns a new [context.Context] with the given labels added.
// A label overwrites a prior label with the same key.
func WithLabels(ctx context.Context, labels LabelSet) context.Context {
parentLabels := labelValue(ctx)
childLabels := make(labelMap, len(parentLabels))
// TODO(matloob): replace the map implementation with something
// more efficient so creating a child context WithLabels doesn't need
// to clone the map.
for k, v := range parentLabels {
childLabels[k] = v
}
for _, label := range labels.list {
childLabels[label.key] = label.value
}
return context.WithValue(ctx, labelContextKey{}, &childLabels)
}

// Labels takes an even number of strings representing key-value pairs
// and makes a [LabelSet] containing them.
// A label overwrites a prior label with the same key.
// Currently only the CPU and goroutine profiles utilize any labels
// information.
// See https://golang.org/issue/23458 for details.
func Labels(args ...string) LabelSet {
if len(args)%2 != 0 {
panic("uneven number of arguments to pprof.Labels")
}
list := make([]label, 0, len(args)/2)
for i := 0; i+1 < len(args); i += 2 {
list = append(list, label{key: args[i], value: args[i+1]})
}
return LabelSet{list: list}
}

// 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 := labelValue(ctx)
v, ok := ctxLabels[key]
return v, ok
}

// 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 := labelValue(ctx)
keys := make([]string, 0, len(ctxLabels))
for k := range ctxLabels {
keys = append(keys, k)
}
slices.Sort(keys)
for _, k := range keys {
if !f(k, ctxLabels[k]) {
break
}
}
}

// SetGoroutineLabels sets the current goroutine's labels to match ctx.
//
// This is a no-op under TinyGo, whose profiler is itself a stub, so nothing
// would read the labels back. The label API itself travels in the context.
func SetGoroutineLabels(ctx context.Context) {}

// Do calls f with a copy of the parent context with the given labels added to
// the parent's label map. Goroutines spawned while executing f will inherit
// the augmented label-set. 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)
}
6 changes: 6 additions & 0 deletions src/runtime/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ func (f *Func) FileLine(pc uintptr) (file string, line int) {
return "", 0
}

// Entry returns the entry address of the function. It is stubbed like the
// rest of runtime.Func, so that callers such as testify/mock compile.
func (f *Func) Entry() uintptr {
return 0
}

func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
return 0, "", 0, false
}
Expand Down
Loading