panic.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. // Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  2. package lambda
  3. import (
  4. "fmt"
  5. "runtime"
  6. "strings"
  7. "github.com/aws/aws-lambda-go/lambda/messages"
  8. )
  9. type panicInfo struct {
  10. Message string // Value passed to panic call, converted to string
  11. StackTrace []*messages.InvokeResponse_Error_StackFrame // Stack trace of the panic
  12. }
  13. func getPanicInfo(value interface{}) panicInfo {
  14. message := getPanicMessage(value)
  15. stack := getPanicStack()
  16. return panicInfo{Message: message, StackTrace: stack}
  17. }
  18. func getPanicMessage(value interface{}) string {
  19. return fmt.Sprintf("%v", value)
  20. }
  21. var defaultErrorFrameCount = 32
  22. func getPanicStack() []*messages.InvokeResponse_Error_StackFrame {
  23. s := make([]uintptr, defaultErrorFrameCount)
  24. const framesToHide = 3 // this (getPanicStack) -> getPanicInfo -> handler defer func
  25. n := runtime.Callers(framesToHide, s)
  26. if n == 0 {
  27. return make([]*messages.InvokeResponse_Error_StackFrame, 0)
  28. }
  29. s = s[:n]
  30. return convertStack(s)
  31. }
  32. func convertStack(s []uintptr) []*messages.InvokeResponse_Error_StackFrame {
  33. var converted []*messages.InvokeResponse_Error_StackFrame
  34. frames := runtime.CallersFrames(s)
  35. for {
  36. frame, more := frames.Next()
  37. formattedFrame := formatFrame(frame)
  38. converted = append(converted, formattedFrame)
  39. if !more {
  40. break
  41. }
  42. }
  43. return converted
  44. }
  45. func formatFrame(inputFrame runtime.Frame) *messages.InvokeResponse_Error_StackFrame {
  46. path := inputFrame.File
  47. line := int32(inputFrame.Line)
  48. label := inputFrame.Function
  49. // Strip GOPATH from path by counting the number of seperators in label & path
  50. //
  51. // For example given this:
  52. // GOPATH = /home/user
  53. // path = /home/user/src/pkg/sub/file.go
  54. // label = pkg/sub.Type.Method
  55. //
  56. // We want to set:
  57. // path = pkg/sub/file.go
  58. // label = Type.Method
  59. i := len(path)
  60. for n, g := 0, strings.Count(label, "/")+2; n < g; n++ {
  61. i = strings.LastIndex(path[:i], "/")
  62. if i == -1 {
  63. // Something went wrong and path has less seperators than we expected
  64. // Abort and leave i as -1 to counteract the +1 below
  65. break
  66. }
  67. }
  68. path = path[i+1:] // Trim the initial /
  69. // Strip the path from the function name as it's already in the path
  70. label = label[strings.LastIndex(label, "/")+1:]
  71. // Likewise strip the package name
  72. label = label[strings.Index(label, ".")+1:]
  73. return &messages.InvokeResponse_Error_StackFrame{
  74. Path: path,
  75. Line: line,
  76. Label: label,
  77. }
  78. }