forked from luontola/gospec
-
Notifications
You must be signed in to change notification settings - Fork 1
/
recover.go
72 lines (62 loc) · 1.65 KB
/
recover.go
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
// Copyright © 2009-2011 Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0
package gospec
import (
"fmt"
"runtime"
)
type exception struct {
Cause interface{}
StackTrace []*Location
}
func (this *exception) ToError() *Error {
return newError(OtherError, this.String(), "", this.StackTrace)
}
func (this *exception) String() string {
return fmt.Sprintf("panic: %v", this.Cause)
}
func recoverOnPanic(f func()) (err *exception) {
defer func() {
if cause := recover(); cause != nil {
callers := stackTraceOfPanic()
callers = cutStackTraceAt(recoverOnPanic, callers)
err = &exception{cause, asLocationArray(callers)}
}
}()
f()
return
}
func stackTraceOfPanic() []uintptr {
// When changing this method, remember to test the array resizing code
// by temporarily setting the initial array size to 1.
callers := make([]uintptr, 16)
for {
// Magic number for correct operation when called from recoverOnPanic()
count := runtime.Callers(4, callers)
if count == len(callers) {
callers = make([]uintptr, len(callers)*2)
} else {
callers = callers[0:count]
break
}
}
return callers
}
func cutStackTraceAt(cutpoint_ interface{}, callers []uintptr) []uintptr {
cutpoint := functionToFunc(cutpoint_).Entry()
for i, ptr := range callers {
current := runtime.FuncForPC(ptr).Entry()
if current == cutpoint {
return callers[0:i]
}
}
return callers
}
func asLocationArray(pcs []uintptr) []*Location {
result := make([]*Location, len(pcs))
for i, pc := range pcs {
result[i] = locationForPC(pc)
}
return result
}