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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
// addOneOpenDeferFrame scans the stack (in gentraceback order, from inner frames to
// outer frames) for the first frame (if any) with open-coded defers. If it finds
// one, it adds a single entry to the defer chain for that frame. The entry added
// represents all the defers in the associated open defer frame, and is sorted in
// order with respect to any non-open-coded defers.
//
// addOneOpenDeferFrame stops (possibly without adding a new entry) if it encounters
// an in-progress open defer entry. An in-progress open defer entry means there has
// been a new panic because of a defer in the associated frame. addOneOpenDeferFrame
// does not add an open defer entry past a started entry, because that started entry
// still needs to finished, and addOneOpenDeferFrame will be called when that started
// entry is completed. The defer removal loop in gopanic() similarly stops at an
// in-progress defer entry. Together, addOneOpenDeferFrame and the defer removal loop
// ensure the invariant that there is no open defer entry further up the stack than
// an in-progress defer, and also that the defer removal loop is guaranteed to remove
// all not-in-progress open defer entries from the defer chain.
//
// If sp is non-nil, addOneOpenDeferFrame starts the stack scan from the frame
// specified by sp. If sp is nil, it uses the sp from the current defer record (which
// has just been finished). Hence, it continues the stack scan from the frame of the
// defer that just finished. It skips any frame that already has a (not-in-progress)
// open-coded _defer record in the defer chain.
//
// Note: All entries of the defer chain (including this new open-coded entry) have
// their pointers (including sp) adjusted properly if the stack moves while
// running deferred functions. Also, it is safe to pass in the sp arg (which is
// the direct result of calling getcallersp()), because all pointer variables
// (including arguments) are adjusted as needed during stack copies.
//
// gp *g 当前goroutine
// pc uintptr 当前发生panic的下一条指令处
// sp unsafe.Pointer 当前发生panic的函数栈顶
func addOneOpenDeferFrame(gp *g, pc uintptr, sp unsafe.Pointer) {
var prevDefer *_defer // goroutine的上一个defer结构
if sp == nil {
prevDefer = gp._defer
pc = prevDefer.framepc
sp = unsafe.Pointer(prevDefer.sp)
}
systemstack(func() {
gentraceback(pc, uintptr(sp), 0, gp, 0, nil, 0x7fffffff,
func(frame *stkframe, unused unsafe.Pointer) bool {
if prevDefer != nil && prevDefer.sp == frame.sp {
// Skip the frame for the previous defer that
// we just finished (and was used to set
// where we restarted the stack scan)
return true
}
f := frame.fn
fd := funcdata(f, _FUNCDATA_OpenCodedDeferInfo)
if fd == nil {
return true
}
// Insert the open defer record in the
// chain, in order sorted by sp.
d := gp._defer
var prev *_defer
for d != nil {
dsp := d.sp
if frame.sp < dsp {
break
}
if frame.sp == dsp {
if !d.openDefer {
throw("duplicated defer entry")
}
// Don't add any record past an
// in-progress defer entry. We don't
// need it, and more importantly, we
// want to keep the invariant that
// there is no open defer entry
// passed an in-progress entry (see
// header comment).
if d.started {
return false
}
return true
}
prev = d
d = d.link
}
if frame.fn.deferreturn == 0 {
throw("missing deferreturn")
}
d1 := newdefer()
d1.openDefer = true
d1._panic = nil
// These are the pc/sp to set after we've
// run a defer in this frame that did a
// recover. We return to a special
// deferreturn that runs any remaining
// defers and then returns from the
// function.
d1.pc = frame.fn.entry() + uintptr(frame.fn.deferreturn)
d1.varp = frame.varp
d1.fd = fd
// Save the SP/PC associated with current frame,
// so we can continue stack trace later if needed.
d1.framepc = frame.pc
d1.sp = frame.sp
d1.link = d
if prev == nil {
gp._defer = d1
} else {
prev.link = d1
}
// Stop stack scanning after adding one open defer record
return false
},
nil, 0)
})
}
|