blob: b4a62f908155de94182d553c5dfc6262b2b8f9cd [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package core
import (
"context"
"regexp"
"runtime"
"runtime/pprof"
"sort"
"strconv"
"unsafe"
)
type label struct {
key string
value string
}
type LabelSet struct {
list []label
}
type labelMap struct {
LabelSet
}
type labelMap19 map[string]string
type labelContextKey struct{}
//go:linkname runtimeGetProfLabel runtime/pprof.runtime_getProfLabel
func runtimeGetProfLabel() unsafe.Pointer
//go:linkname runtimeSetProfLabel runtime/pprof.runtime_setProfLabel
func runtimeSetProfLabel(label unsafe.Pointer)
func setGoroutineLabelsInternal(ctx context.Context) {
if isGoVersionLMoreThan120(runtime.Version()) {
ctxLabels, _ := ctx.Value(labelContextKey{}).(*labelMap)
runtimeSetProfLabel(unsafe.Pointer(ctxLabels))
return
}
ctxLabels, _ := ctx.Value(labelContextKey{}).(*labelMap19)
runtimeSetProfLabel(unsafe.Pointer(ctxLabels))
}
func labelValue(ctx context.Context) labelMap19 {
labels, _ := ctx.Value(labelContextKey{}).(*labelMap19)
if labels == nil {
return labelMap19(nil)
}
return *labels
}
func WithLabels(ctx context.Context, s LabelSet) context.Context {
if isGoVersionLMoreThan120(runtime.Version()) {
ctx = context.WithValue(ctx, labelContextKey{}, &labelMap{s})
return ctx
}
return withLabels19(ctx, s)
}
func withLabels19(ctx context.Context, labels LabelSet) context.Context {
childLabels := make(labelMap19)
parentLabels := labelValue(ctx)
for k, v := range parentLabels {
childLabels[k] = v
}
for _, label := range labels.list {
childLabels[label.key] = label.value
}
return context.WithValue(ctx, labelContextKey{}, &childLabels)
}
func GetNowLabelSet() LabelSet {
pl := LabelSet{
list: make([]label, 0),
}
p := runtimeGetProfLabel()
if p != nil {
version := runtime.Version()
if !isGoVersionLMoreThan120(version) {
// Go1.19:map[string]string -> []label
m := *(*labelMap19)(p)
pl.list = make([]label, 0, len(m))
for k, v := range m {
pl.list = append(pl.list, label{key: k, value: v})
}
} else {
lm := (*labelMap)(p)
pl.list = lm.list
}
}
return pl
}
// isGoVersionLMoreThan120 parses version strings like "go1.19.8"
func isGoVersionLMoreThan120(version string) bool {
re := regexp.MustCompile(`go(\d+)\.(\d+)`)
sub := re.FindStringSubmatch(version)
if len(sub) != 3 {
return false
}
major, err1 := strconv.Atoi(sub[1])
minor, err2 := strconv.Atoi(sub[2])
if err1 != nil || err2 != nil {
return false
}
if major < 1 {
return false
}
if major > 1 {
return true
}
return minor >= 20
}
func (m *ProfileManager) AddSkyLabels(traceID, segmentID string, spanID int32) interface{} {
pl := GetNowLabelSet()
re := UpdateTraceLabels(pl, TraceLabel, traceID, SegmentLabel, segmentID, SpanLabel, parseString(spanID))
return &re
}
func (m *ProfileManager) TurnToPprofLabel(l *LabelSet) pprof.LabelSet {
li := l.List()
if len(li) == 0 {
return pprof.LabelSet{}
}
re := pprof.Labels(li...)
return re
}
func UpdateTraceLabels(s LabelSet, args ...string) LabelSet {
if len(args)%2 != 0 {
panic("uneven number of arguments to profile.UpdateTraceLabels")
}
// add first
for i := 0; i < len(args); i += 2 {
s.list = append(s.list, label{key: args[i], value: args[i+1]})
}
// sort
sort.SliceStable(s.list, func(i, j int) bool {
return s.list[i].key < s.list[j].key
})
// remove duplicates
deduped := make([]label, 0, len(s.list))
for i, lbl := range s.list {
if i == 0 || lbl.key != s.list[i-1].key {
deduped = append(deduped, lbl)
} else {
deduped[len(deduped)-1] = lbl
}
}
s.list = deduped
return s
}
func (s *LabelSet) List() []string {
var ret []string
for _, v := range s.list {
ret = append(ret, v.key, v.value)
}
return ret
}
func SetGoroutineLabels(s *LabelSet) {
if s.IsEmpty() {
var c = context.Background()
setGoroutineLabelsInternal(c)
return
}
var c = context.Background()
l := *s
c = WithLabels(c, l)
setGoroutineLabelsInternal(c)
}
func extractSkyInternalLabels(s LabelSet) LabelSet {
re := LabelSet{list: make([]label, 0)}
for _, l := range s.list {
if l.key == SpanLabel || l.key == SegmentLabel || l.key == TraceLabel || l.key == MinDurationLabel {
re.list = append(re.list, l)
}
}
return re
}
// GetNowLabels Expose to operator
func (m *ProfileManager) GetNowLabels() interface{} {
row := GetNowLabelSet()
filter := extractSkyInternalLabels(row)
re := m.TurnToPprofLabel(&filter)
return re
}
func (s *LabelSet) IsEmpty() bool {
if s == nil || s.list == nil {
return true
}
return len(s.list) == 0
}