github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/convert/parser.go (about) 1 package convert 2 3 import ( 4 "bufio" 5 "bytes" 6 "compress/gzip" 7 "fmt" 8 "io" 9 "strconv" 10 11 "google.golang.org/protobuf/proto" 12 13 "github.com/pyroscope-io/pyroscope/pkg/storage/tree" 14 ) 15 16 func ParseTreeNoDict(r io.Reader, cb func(name []byte, val int)) error { 17 t, err := tree.DeserializeNoDict(r) 18 if err != nil { 19 return err 20 } 21 t.Iterate(func(name []byte, val uint64) { 22 if len(name) > 2 && val != 0 { 23 cb(name[2:], int(val)) 24 } 25 }) 26 return nil 27 } 28 29 var gzipMagicBytes = []byte{0x1f, 0x8b} 30 31 // format is pprof. See https://github.com/google/pprof/blob/master/proto/profile.proto 32 func ParsePprof(r io.Reader) (*tree.Profile, error) { 33 // this allows us to support both gzipped and not gzipped pprof 34 // TODO: this might be allocating too much extra memory, maybe optimize later 35 bufioReader := bufio.NewReader(r) 36 header, err := bufioReader.Peek(2) 37 if err != nil { 38 return nil, fmt.Errorf("unable to read profile file header: %w", err) 39 } 40 41 if header[0] == gzipMagicBytes[0] && header[1] == gzipMagicBytes[1] { 42 r, err = gzip.NewReader(bufioReader) 43 if err != nil { 44 return nil, err 45 } 46 } else { 47 r = bufioReader 48 } 49 50 b, err := io.ReadAll(r) 51 if err != nil { 52 return nil, err 53 } 54 55 profile := &tree.Profile{} 56 if err := proto.Unmarshal(b, profile); err != nil { 57 return nil, err 58 } 59 60 return profile, nil 61 } 62 63 // format: 64 // stack-trace-foo 1 65 // stack-trace-bar 2 66 func ParseGroups(r io.Reader, cb func(name []byte, val int)) error { 67 scanner := bufio.NewScanner(r) 68 for scanner.Scan() { 69 if err := scanner.Err(); err != nil { 70 return err 71 } 72 73 line := scanner.Bytes() 74 line2 := make([]byte, len(line)) 75 copy(line2, line) 76 77 index := bytes.LastIndexByte(line2, byte(' ')) 78 if index == -1 { 79 continue 80 } 81 stacktrace := line2[:index] 82 count := line2[index+1:] 83 84 i, err := strconv.Atoi(string(count)) 85 if err != nil { 86 return err 87 } 88 cb(stacktrace, i) 89 } 90 return nil 91 } 92 93 // format: 94 // stack-trace-foo 95 // stack-trace-bar 96 // stack-trace-bar 97 func ParseIndividualLines(r io.Reader, cb func(name []byte, val int)) error { 98 groups := make(map[string]int) 99 scanner := bufio.NewScanner(r) 100 // scanner.Buffer(make([]byte, bufio.MaxScanTokenSize*100), bufio.MaxScanTokenSize*100) 101 // scanner.Split(bufio.ScanLines) 102 for scanner.Scan() { 103 if err := scanner.Err(); err != nil { 104 return err 105 } 106 key := scanner.Text() 107 if _, ok := groups[key]; !ok { 108 groups[key] = 0 109 } 110 groups[key]++ 111 } 112 113 if err := scanner.Err(); err != nil { 114 return err 115 } 116 117 for k, v := range groups { 118 if k != "" { 119 cb([]byte(k), v) 120 } 121 } 122 123 return nil 124 }