...

Source file src/github.com/go-errors/errors/parse_panic.go

Documentation: github.com/go-errors/errors

     1  package errors
     2  
     3  import (
     4  	"strconv"
     5  	"strings"
     6  )
     7  
     8  type uncaughtPanic struct{ message string }
     9  
    10  func (p uncaughtPanic) Error() string {
    11  	return p.message
    12  }
    13  
    14  // ParsePanic allows you to get an error object from the output of a go program
    15  // that panicked. This is particularly useful with https://github.com/mitchellh/panicwrap.
    16  func ParsePanic(text string) (*Error, error) {
    17  	lines := strings.Split(text, "\n")
    18  
    19  	state := "start"
    20  
    21  	var message string
    22  	var stack []StackFrame
    23  
    24  	for i := 0; i < len(lines); i++ {
    25  		line := lines[i]
    26  
    27  		if state == "start" {
    28  			if strings.HasPrefix(line, "panic: ") {
    29  				message = strings.TrimPrefix(line, "panic: ")
    30  				state = "seek"
    31  			} else {
    32  				return nil, Errorf("bugsnag.panicParser: Invalid line (no prefix): %s", line)
    33  			}
    34  
    35  		} else if state == "seek" {
    36  			if strings.HasPrefix(line, "goroutine ") && strings.HasSuffix(line, "[running]:") {
    37  				state = "parsing"
    38  			}
    39  
    40  		} else if state == "parsing" {
    41  			if line == "" {
    42  				state = "done"
    43  				break
    44  			}
    45  			createdBy := false
    46  			if strings.HasPrefix(line, "created by ") {
    47  				line = strings.TrimPrefix(line, "created by ")
    48  				createdBy = true
    49  			}
    50  
    51  			i++
    52  
    53  			if i >= len(lines) {
    54  				return nil, Errorf("bugsnag.panicParser: Invalid line (unpaired): %s", line)
    55  			}
    56  
    57  			frame, err := parsePanicFrame(line, lines[i], createdBy)
    58  			if err != nil {
    59  				return nil, err
    60  			}
    61  
    62  			stack = append(stack, *frame)
    63  			if createdBy {
    64  				state = "done"
    65  				break
    66  			}
    67  		}
    68  	}
    69  
    70  	if state == "done" || state == "parsing" {
    71  		return &Error{Err: uncaughtPanic{message}, frames: stack}, nil
    72  	}
    73  	return nil, Errorf("could not parse panic: %v", text)
    74  }
    75  
    76  // The lines we're passing look like this:
    77  //
    78  //     main.(*foo).destruct(0xc208067e98)
    79  //             /0/go/src/github.com/bugsnag/bugsnag-go/pan/main.go:22 +0x151
    80  func parsePanicFrame(name string, line string, createdBy bool) (*StackFrame, error) {
    81  	idx := strings.LastIndex(name, "(")
    82  	if idx == -1 && !createdBy {
    83  		return nil, Errorf("bugsnag.panicParser: Invalid line (no call): %s", name)
    84  	}
    85  	if idx != -1 {
    86  		name = name[:idx]
    87  	}
    88  	pkg := ""
    89  
    90  	if lastslash := strings.LastIndex(name, "/"); lastslash >= 0 {
    91  		pkg += name[:lastslash] + "/"
    92  		name = name[lastslash+1:]
    93  	}
    94  	if period := strings.Index(name, "."); period >= 0 {
    95  		pkg += name[:period]
    96  		name = name[period+1:]
    97  	}
    98  
    99  	name = strings.Replace(name, "ยท", ".", -1)
   100  
   101  	if !strings.HasPrefix(line, "\t") {
   102  		return nil, Errorf("bugsnag.panicParser: Invalid line (no tab): %s", line)
   103  	}
   104  
   105  	idx = strings.LastIndex(line, ":")
   106  	if idx == -1 {
   107  		return nil, Errorf("bugsnag.panicParser: Invalid line (no line number): %s", line)
   108  	}
   109  	file := line[1:idx]
   110  
   111  	number := line[idx+1:]
   112  	if idx = strings.Index(number, " +"); idx > -1 {
   113  		number = number[:idx]
   114  	}
   115  
   116  	lno, err := strconv.ParseInt(number, 10, 32)
   117  	if err != nil {
   118  		return nil, Errorf("bugsnag.panicParser: Invalid line (bad line number): %s", line)
   119  	}
   120  
   121  	return &StackFrame{
   122  		File:       file,
   123  		LineNumber: int(lno),
   124  		Package:    pkg,
   125  		Name:       name,
   126  	}, nil
   127  }
   128  

View as plain text