| // 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 |
| } |