| // 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 |
| } |