github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/convert/perf/script.go (about) 1 package perf 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "regexp" 8 "strconv" 9 ) 10 11 var reEventStart = regexp.MustCompile("^(\\S.+?)\\s+(\\d+)/*(\\d+)*\\s+\\S.+") 12 var errEventStartRegexMismatch = fmt.Errorf("reEventStart mismatch") 13 var reStackFrame = regexp.MustCompile("^\\s*(\\w+)\\s*(.+) \\((\\S*)\\)") 14 var errStackFrameRegexMismatch = fmt.Errorf("reStackFrame mismatch") 15 var sep = []byte{'\n'} 16 17 type ScriptParser struct { 18 lines [][]byte 19 lineIndex int 20 } 21 22 func NewScriptParser(buf []byte) *ScriptParser { 23 return &ScriptParser{ 24 lines: bytes.Split(buf, sep), 25 lineIndex: 0, 26 } 27 } 28 func (p *ScriptParser) nextLine() ([]byte, error) { 29 if p.lineIndex < len(p.lines) { 30 ret := p.lines[p.lineIndex] 31 p.lineIndex++ 32 return ret, nil 33 } 34 return nil, io.EOF 35 } 36 37 func (p *ScriptParser) ParseEvents() ([][][]byte, error) { 38 stacks := make([][][]byte, 0, 256) 39 for { 40 stack, err := p.ParseEvent() 41 if err != nil { 42 if err == io.EOF { 43 break 44 } 45 return nil, err 46 } 47 stacks = append(stacks, stack) 48 } 49 return stacks, nil 50 } 51 52 func (p *ScriptParser) ParseEvent() ([][]byte, error) { 53 line, err := p.nextLine() 54 if err != nil { 55 return nil, err 56 } 57 stack := make([][]byte, 0, 16) 58 comm, _, _, err := parseEventStart(line) 59 if err != nil { 60 if len(line) == 0 && p.lineIndex >= len(p.lines) { 61 return nil, io.EOF 62 } 63 return nil, err 64 } 65 var sym []byte 66 for { 67 line, err = p.nextLine() 68 if err != nil { 69 return nil, err 70 } 71 if parseEventEnd(line) { 72 break 73 } 74 _, sym, _, err = parseStackFrame(line) 75 if err != nil { 76 return nil, err 77 } 78 stack = append(stack, sym) 79 } 80 stack = append(stack, comm) 81 for i, j := 0, len(stack)-1; i < j; i, j = i+1, j-1 { 82 stack[i], stack[j] = stack[j], stack[i] 83 } 84 return stack, nil 85 } 86 87 func IsPerfScript(buf []byte) bool { 88 ls := bytes.SplitN(buf, sep, 2) 89 if len(ls) < 2 { 90 return false 91 } 92 l := ls[0] 93 _, _, _, err := parseEventStart(l) 94 return err == nil 95 } 96 97 func parseEventStart(line []byte) ([]byte, int, int, error) { 98 res := reEventStart.FindSubmatch(line) 99 if res == nil { 100 return nil, 0, 0, errEventStartRegexMismatch 101 } 102 comm := res[1] 103 tid := 0 104 pid, err := strconv.Atoi(string(res[2])) 105 if err != nil { 106 return nil, 0, 0, err 107 } 108 if res[3] != nil { 109 tid, err = strconv.Atoi(string(res[3])) 110 if err != nil { 111 return nil, 0, 0, err 112 } 113 } 114 return comm, pid, tid, nil 115 } 116 117 func parseEventEnd(line []byte) bool { 118 return len(line) == 0 119 } 120 121 func parseStackFrame(line []byte) ([]byte, []byte, []byte, error) { 122 res := reStackFrame.FindSubmatch(line) 123 if res == nil { 124 return nil, nil, nil, errStackFrameRegexMismatch 125 } 126 adr := res[1] 127 sym := res[2] 128 mod := res[3] 129 return adr, sym, mod, nil 130 }