mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: use conservative scanning for debug calls
A debugger can inject a call at almost any PC, which causes significant complications with stack scanning and growth. Currently, the runtime solves this using precise stack maps and register maps at nearly all PCs, but these extra maps require roughly 5% of the binary. These extra maps were originally considered worth this space because they were intended to be used for non-cooperative preemption, but are now used only for debug call injection. This CL switches from using precise maps to instead using conservative frame scanning, much like how non-cooperative preemption works. When a call is injected, the runtime flushes all potential pointer registers to the stack, and then treats that frame as well as the interrupted frame conservatively. The limitation of conservative frame scanning is that we cannot grow the goroutine stack. That's doable because the previous CL switched to performing debug calls on a new goroutine, where they are free to grow the stack. With this CL, there are no remaining uses of precise register maps (though we still use the unsafe-point information that's encoded in the register map PCDATA stream), and stack maps are only used at call sites. For #36365. Change-Id: Ie217b6711f3741ccc437552d8ff88f961a73cee0 Reviewed-on: https://go-review.googlesource.com/c/go/+/229300 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
parent
3633d2c545
commit
57d751370c
4 changed files with 19 additions and 28 deletions
|
|
@ -1216,29 +1216,8 @@ func getStackMap(frame *stkframe, cache *pcvalueCache, debug bool) (locals, args
|
|||
minsize = sys.MinFrameSize
|
||||
}
|
||||
if size > minsize {
|
||||
var stkmap *stackmap
|
||||
stackid := pcdata
|
||||
if f.funcID != funcID_debugCallV1 {
|
||||
stkmap = (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
|
||||
} else {
|
||||
// debugCallV1's stack map is the register map
|
||||
// at its call site.
|
||||
callerPC := frame.lr
|
||||
caller := findfunc(callerPC)
|
||||
if !caller.valid() {
|
||||
println("runtime: debugCallV1 called by unknown caller", hex(callerPC))
|
||||
throw("bad debugCallV1")
|
||||
}
|
||||
stackid = int32(-1)
|
||||
if callerPC != caller.entry {
|
||||
callerPC--
|
||||
stackid = pcdatavalue(caller, _PCDATA_RegMapIndex, callerPC, cache)
|
||||
}
|
||||
if stackid == -1 {
|
||||
stackid = 0 // in prologue
|
||||
}
|
||||
stkmap = (*stackmap)(funcdata(caller, _FUNCDATA_RegPointerMaps))
|
||||
}
|
||||
stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
|
||||
if stkmap == nil || stkmap.n <= 0 {
|
||||
print("runtime: frame ", funcname(f), " untyped locals ", hex(frame.varp-size), "+", hex(size), "\n")
|
||||
throw("missing stackmap")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue