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  }