blob: d26e3c26b95930a2f47535d58e883e17bd8bf67a (
plain)
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
|
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build amd64
package runtime
import "unsafe"
const (
debugCallSystemStack = "executing on Go runtime stack"
debugCallUnknownFunc = "call from unknown function"
debugCallRuntime = "call from within the Go runtime"
debugCallUnsafePoint = "call not at safe point"
)
func debugCallV1()
func debugCallPanicked(val interface{})
// debugCallCheck checks whether it is safe to inject a debugger
// function call with return PC pc. If not, it returns a string
// explaining why.
//
//go:nosplit
func debugCallCheck(pc uintptr) string {
// No user calls from the system stack.
if getg() != getg().m.curg {
return debugCallSystemStack
}
if sp := getcallersp(); !(getg().stack.lo < sp && sp <= getg().stack.hi) {
// Fast syscalls (nanotime) and racecall switch to the
// g0 stack without switching g. We can't safely make
// a call in this state. (We can't even safely
// systemstack.)
return debugCallSystemStack
}
// Switch to the system stack to avoid overflowing the user
// stack.
var ret string
systemstack(func() {
f := findfunc(pc)
if !f.valid() {
ret = debugCallUnknownFunc
return
}
// Disallow calls from the runtime. We could
// potentially make this condition tighter (e.g., not
// when locks are held), but there are enough tightly
// coded sequences (e.g., defer handling) that it's
// better to play it safe.
if name, pfx := funcname(f), "runtime."; len(name) > len(pfx) && name[:len(pfx)] == pfx {
ret = debugCallRuntime
return
}
// Look up PC's register map.
pcdata := int32(-1)
if pc != f.entry {
pc--
pcdata = pcdatavalue(f, _PCDATA_RegMapIndex, pc, nil)
}
if pcdata == -1 {
pcdata = 0 // in prologue
}
stkmap := (*stackmap)(funcdata(f, _FUNCDATA_RegPointerMaps))
if pcdata == -2 || stkmap == nil {
// Not at a safe point.
ret = debugCallUnsafePoint
return
}
})
return ret
}
// debugCallWrap pushes a defer to recover from panics in debug calls
// and then calls the dispatching function at PC dispatch.
func debugCallWrap(dispatch uintptr) {
var dispatchF func()
dispatchFV := funcval{dispatch}
*(*unsafe.Pointer)(unsafe.Pointer(&dispatchF)) = noescape(unsafe.Pointer(&dispatchFV))
var ok bool
defer func() {
if !ok {
err := recover()
debugCallPanicked(err)
}
}()
dispatchF()
ok = true
}
|