github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/helper/profile/pyroscope/raw/profile.go (about) 1 // Copyright 2023 iLogtail Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package raw 16 17 import ( 18 "bufio" 19 "bytes" 20 "context" 21 "encoding/json" 22 "strconv" 23 "strings" 24 "time" 25 26 "github.com/cespare/xxhash/v2" 27 "github.com/pyroscope-io/pyroscope/pkg/structs/transporttrie" 28 29 "github.com/alibaba/ilogtail/pkg/helper" 30 "github.com/alibaba/ilogtail/pkg/helper/profile" 31 "github.com/alibaba/ilogtail/pkg/protocol" 32 ) 33 34 type Profile struct { 35 RawData []byte 36 Format profile.Format 37 38 logs []*protocol.Log // v1 result 39 } 40 41 func NewRawProfile(data []byte, format profile.Format) *Profile { 42 return &Profile{ 43 RawData: data, 44 Format: format, 45 } 46 } 47 48 func (p *Profile) Parse(ctx context.Context, meta *profile.Meta, tags map[string]string) (logs []*protocol.Log, err error) { 49 cb := p.extractProfileV1(meta, tags) 50 if err := p.doParse(cb); err != nil { 51 return nil, err 52 } 53 return p.logs, nil 54 } 55 56 func (p *Profile) doParse(cb func([]byte, int)) error { 57 r := bytes.NewReader(p.RawData) 58 switch p.Format { 59 case profile.FormatTrie: 60 err := transporttrie.IterateRaw(r, make([]byte, 0, 256), cb) 61 if err != nil { 62 return err 63 } 64 case profile.FormatGroups: 65 scanner := bufio.NewScanner(r) 66 for scanner.Scan() { 67 if err := scanner.Err(); err != nil { 68 return err 69 } 70 line := scanner.Bytes() 71 index := bytes.LastIndexByte(line, byte(' ')) 72 if index == -1 { 73 continue 74 } 75 stacktrace := line[:index] 76 count := line[index+1:] 77 i, err := strconv.Atoi(string(count)) 78 if err != nil { 79 return err 80 } 81 cb(stacktrace, i) 82 } 83 } 84 return nil 85 } 86 87 func (p *Profile) extractProfileV1(meta *profile.Meta, tags map[string]string) func([]byte, int) { 88 profileID := profile.GetProfileID(meta) 89 for k, v := range tags { 90 meta.Tags[k] = v 91 } 92 labels, _ := json.Marshal(meta.Tags) 93 return func(k []byte, v int) { 94 name, stack := p.extractNameAndStacks(k, meta.SpyName) 95 stackID := strconv.FormatUint(xxhash.Sum64(k), 16) 96 var content []*protocol.Log_Content 97 u := meta.Units 98 if meta.Units == profile.SamplesUnits { 99 u = profile.NanosecondsUnit 100 v *= int(time.Second.Nanoseconds() / int64(meta.SampleRate)) 101 } 102 content = append(content, 103 &protocol.Log_Content{ 104 Key: "name", 105 Value: name, 106 }, 107 &protocol.Log_Content{ 108 Key: "stack", 109 Value: strings.Join(stack, "\n"), 110 }, 111 &protocol.Log_Content{ 112 Key: "stackID", 113 Value: stackID, 114 }, 115 &protocol.Log_Content{ 116 Key: "language", 117 Value: meta.SpyName, 118 }, 119 &protocol.Log_Content{ 120 Key: "type", 121 Value: profile.DetectProfileType(meta.Units.DetectValueType()).Kind, 122 }, 123 &protocol.Log_Content{ 124 Key: "units", 125 Value: string(u), 126 }, 127 &protocol.Log_Content{ 128 Key: "valueTypes", 129 Value: meta.Units.DetectValueType(), 130 }, 131 &protocol.Log_Content{ 132 Key: "aggTypes", 133 Value: string(meta.AggregationType), 134 }, 135 &protocol.Log_Content{ 136 Key: "dataType", 137 Value: "CallStack", 138 }, 139 &protocol.Log_Content{ 140 Key: "durationNs", 141 Value: strconv.FormatInt(meta.EndTime.Sub(meta.StartTime).Nanoseconds(), 10), 142 }, 143 &protocol.Log_Content{ 144 Key: "profileID", 145 Value: profileID, 146 }, 147 &protocol.Log_Content{ 148 Key: "labels", 149 Value: string(labels), 150 }, 151 &protocol.Log_Content{ 152 Key: "val", 153 Value: strconv.FormatFloat(float64(v), 'f', 2, 64), 154 }, 155 ) 156 157 log := &protocol.Log{ 158 Contents: content, 159 } 160 protocol.SetLogTimeWithNano(log, uint32(meta.StartTime.Unix()), uint32(meta.StartTime.Nanosecond())) 161 p.logs = append(p.logs, log) 162 } 163 164 } 165 166 func (p *Profile) extractNameAndStacks(k []byte, spyName string) (name string, stack []string) { 167 slice := strings.Split(string(k), ";") 168 if len(slice) > 0 && slice[len(slice)-1] == "" { 169 slice = slice[:len(slice)-1] 170 } 171 if len(slice) == 1 { 172 return profile.FormatPositionAndName(slice[len(slice)-1], profile.FormatType(spyName)), []string{} 173 } 174 name = profile.FormatPositionAndName(slice[len(slice)-1], profile.FormatType(spyName)) 175 slice = profile.FormatPostionAndNames(slice[:len(slice)-1], profile.FormatType(spyName)) 176 helper.ReverseStringSlice(slice) 177 return name, slice 178 }