File: panic.go

package info (click to toggle)
golang-code.forgejo-f3-gof3 3.11.0-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 1,952 kB
  • sloc: sh: 100; makefile: 65
file content (98 lines) | stat: -rw-r--r-- 2,088 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT

package util

import (
	"fmt"
	"runtime"
	"strings"
)

var packagePrefix string

func init() {
	_, filename, _, _ := runtime.Caller(0)
	packagePrefix = strings.TrimSuffix(filename, "util/panic.go")
	if packagePrefix == filename {
		// in case the source code file is moved, we can not trim the suffix, the code above should also be updated.
		panic("unable to detect correct package prefix, please update file: " + filename)
	}
}

func getStack() string {
	callersLength := 5
	var callers []uintptr
	var callersCount int
	for {
		callers = make([]uintptr, callersLength)
		callersCount = runtime.Callers(4, callers)
		if callersCount <= 0 {
			panic("runtime.Callers <= 0")
		}
		if callersCount >= callersLength {
			callersLength *= 2
		} else {
			break
		}
	}
	callers = callers[:callersCount]
	frames := runtime.CallersFrames(callers)
	stack := make([]string, 0, 10)
	for {
		frame, more := frames.Next()
		if strings.HasPrefix(frame.File, packagePrefix) {
			file := strings.TrimPrefix(frame.File, packagePrefix)
			if file == "util/panic.go" || file == "util/terminate.go" && more {
				continue
			}
			var function string
			dot := strings.LastIndex(frame.Function, ".")
			if dot >= 0 {
				function = frame.Function[dot+1:]
			}
			stack = append(stack, fmt.Sprintf("%s:%d:%s", file, frame.Line, function))
		}
		if !more {
			break
		}
	}
	return strings.Join(stack, "\n") + "\n"
}

type PanicError interface {
	error
	Stack() string
}

type panicError struct {
	message string
	stack   string
}

func (o panicError) Error() string { return o.message }
func (o panicError) Stack() string { return o.stack }

func PanicToError(fun func()) (err PanicError) {
	defer func() {
		if r := recover(); r != nil {
			recoveredErr, ok := r.(error)
			var message string
			if ok {
				message = recoveredErr.Error()
			}
			err = panicError{
				message: message,
				stack:   getStack(),
			}
			if !ok {
				panic(r)
			}
		}
	}()

	fun()

	return err
}