Source file src/runtime/debugcall.go

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Though the debug call function feature is not enabled on
     6  // ppc64, inserted ppc64 to avoid missing Go declaration error
     7  // for debugCallPanicked while building runtime.test
     8  //go:build amd64 || arm64 || ppc64le || ppc64
     9  
    10  package runtime
    11  
    12  import (
    13  	"internal/abi"
    14  	"unsafe"
    15  )
    16  
    17  const (
    18  	debugCallSystemStack = "executing on Go runtime stack"
    19  	debugCallUnknownFunc = "call from unknown function"
    20  	debugCallRuntime     = "call from within the Go runtime"
    21  	debugCallUnsafePoint = "call not at safe point"
    22  )
    23  
    24  func debugCallV2()
    25  func debugCallPanicked(val any)
    26  
    27  // debugCallCheck checks whether it is safe to inject a debugger
    28  // function call with return PC pc. If not, it returns a string
    29  // explaining why.
    30  //
    31  //go:nosplit
    32  func debugCallCheck(pc uintptr) string {
    33  	// No user calls from the system stack.
    34  	if getg() != getg().m.curg {
    35  		return debugCallSystemStack
    36  	}
    37  	if sp := getcallersp(); !(getg().stack.lo < sp && sp <= getg().stack.hi) {
    38  		// Fast syscalls (nanotime) and racecall switch to the
    39  		// g0 stack without switching g. We can't safely make
    40  		// a call in this state. (We can't even safely
    41  		// systemstack.)
    42  		return debugCallSystemStack
    43  	}
    44  
    45  	// Switch to the system stack to avoid overflowing the user
    46  	// stack.
    47  	var ret string
    48  	systemstack(func() {
    49  		f := findfunc(pc)
    50  		if !f.valid() {
    51  			ret = debugCallUnknownFunc
    52  			return
    53  		}
    54  
    55  		name := funcname(f)
    56  
    57  		switch name {
    58  		case "debugCall32",
    59  			"debugCall64",
    60  			"debugCall128",
    61  			"debugCall256",
    62  			"debugCall512",
    63  			"debugCall1024",
    64  			"debugCall2048",
    65  			"debugCall4096",
    66  			"debugCall8192",
    67  			"debugCall16384",
    68  			"debugCall32768",
    69  			"debugCall65536":
    70  			// These functions are allowed so that the debugger can initiate multiple function calls.
    71  			// See: https://golang.org/cl/161137/
    72  			return
    73  		}
    74  
    75  		// Disallow calls from the runtime. We could
    76  		// potentially make this condition tighter (e.g., not
    77  		// when locks are held), but there are enough tightly
    78  		// coded sequences (e.g., defer handling) that it's
    79  		// better to play it safe.
    80  		if pfx := "runtime."; len(name) > len(pfx) && name[:len(pfx)] == pfx {
    81  			ret = debugCallRuntime
    82  			return
    83  		}
    84  
    85  		// Check that this isn't an unsafe-point.
    86  		if pc != f.entry() {
    87  			pc--
    88  		}
    89  		up := pcdatavalue(f, abi.PCDATA_UnsafePoint, pc)
    90  		if up != abi.UnsafePointSafe {
    91  			// Not at a safe point.
    92  			ret = debugCallUnsafePoint
    93  		}
    94  	})
    95  	return ret
    96  }
    97  
    98  // debugCallWrap starts a new goroutine to run a debug call and blocks
    99  // the calling goroutine. On the goroutine, it prepares to recover
   100  // panics from the debug call, and then calls the call dispatching
   101  // function at PC dispatch.
   102  //
   103  // This must be deeply nosplit because there are untyped values on the
   104  // stack from debugCallV2.
   105  //
   106  //go:nosplit
   107  func debugCallWrap(dispatch uintptr) {
   108  	var lockedExt uint32
   109  	callerpc := getcallerpc()
   110  	gp := getg()
   111  
   112  	// Lock ourselves to the OS thread.
   113  	//
   114  	// Debuggers rely on us running on the same thread until we get to
   115  	// dispatch the function they asked as to.
   116  	//
   117  	// We're going to transfer this to the new G we just created.
   118  	lockOSThread()
   119  
   120  	// Create a new goroutine to execute the call on. Run this on
   121  	// the system stack to avoid growing our stack.
   122  	systemstack(func() {
   123  		// TODO(mknyszek): It would be nice to wrap these arguments in an allocated
   124  		// closure and start the goroutine with that closure, but the compiler disallows
   125  		// implicit closure allocation in the runtime.
   126  		fn := debugCallWrap1
   127  		newg := newproc1(*(**funcval)(unsafe.Pointer(&fn)), gp, callerpc)
   128  		args := &debugCallWrapArgs{
   129  			dispatch: dispatch,
   130  			callingG: gp,
   131  		}
   132  		newg.param = unsafe.Pointer(args)
   133  
   134  		// Transfer locked-ness to the new goroutine.
   135  		// Save lock state to restore later.
   136  		mp := gp.m
   137  		if mp != gp.lockedm.ptr() {
   138  			throw("inconsistent lockedm")
   139  		}
   140  		// Save the external lock count and clear it so
   141  		// that it can't be unlocked from the debug call.
   142  		// Note: we already locked internally to the thread,
   143  		// so if we were locked before we're still locked now.
   144  		lockedExt = mp.lockedExt
   145  		mp.lockedExt = 0
   146  
   147  		mp.lockedg.set(newg)
   148  		newg.lockedm.set(mp)
   149  		gp.lockedm = 0
   150  
   151  		// Mark the calling goroutine as being at an async
   152  		// safe-point, since it has a few conservative frames
   153  		// at the bottom of the stack. This also prevents
   154  		// stack shrinks.
   155  		gp.asyncSafePoint = true
   156  
   157  		// Stash newg away so we can execute it below (mcall's
   158  		// closure can't capture anything).
   159  		gp.schedlink.set(newg)
   160  	})
   161  
   162  	// Switch to the new goroutine.
   163  	mcall(func(gp *g) {
   164  		// Get newg.
   165  		newg := gp.schedlink.ptr()
   166  		gp.schedlink = 0
   167  
   168  		// Park the calling goroutine.
   169  		trace := traceAcquire()
   170  		casGToWaiting(gp, _Grunning, waitReasonDebugCall)
   171  		if trace.ok() {
   172  			trace.GoPark(traceBlockDebugCall, 1)
   173  			traceRelease(trace)
   174  		}
   175  		dropg()
   176  
   177  		// Directly execute the new goroutine. The debug
   178  		// protocol will continue on the new goroutine, so
   179  		// it's important we not just let the scheduler do
   180  		// this or it may resume a different goroutine.
   181  		execute(newg, true)
   182  	})
   183  
   184  	// We'll resume here when the call returns.
   185  
   186  	// Restore locked state.
   187  	mp := gp.m
   188  	mp.lockedExt = lockedExt
   189  	mp.lockedg.set(gp)
   190  	gp.lockedm.set(mp)
   191  
   192  	// Undo the lockOSThread we did earlier.
   193  	unlockOSThread()
   194  
   195  	gp.asyncSafePoint = false
   196  }
   197  
   198  type debugCallWrapArgs struct {
   199  	dispatch uintptr
   200  	callingG *g
   201  }
   202  
   203  // debugCallWrap1 is the continuation of debugCallWrap on the callee
   204  // goroutine.
   205  func debugCallWrap1() {
   206  	gp := getg()
   207  	args := (*debugCallWrapArgs)(gp.param)
   208  	dispatch, callingG := args.dispatch, args.callingG
   209  	gp.param = nil
   210  
   211  	// Dispatch call and trap panics.
   212  	debugCallWrap2(dispatch)
   213  
   214  	// Resume the caller goroutine.
   215  	getg().schedlink.set(callingG)
   216  	mcall(func(gp *g) {
   217  		callingG := gp.schedlink.ptr()
   218  		gp.schedlink = 0
   219  
   220  		// Unlock this goroutine from the M if necessary. The
   221  		// calling G will relock.
   222  		if gp.lockedm != 0 {
   223  			gp.lockedm = 0
   224  			gp.m.lockedg = 0
   225  		}
   226  
   227  		// Switch back to the calling goroutine. At some point
   228  		// the scheduler will schedule us again and we'll
   229  		// finish exiting.
   230  		trace := traceAcquire()
   231  		casgstatus(gp, _Grunning, _Grunnable)
   232  		if trace.ok() {
   233  			trace.GoSched()
   234  			traceRelease(trace)
   235  		}
   236  		dropg()
   237  		lock(&sched.lock)
   238  		globrunqput(gp)
   239  		unlock(&sched.lock)
   240  
   241  		trace = traceAcquire()
   242  		casgstatus(callingG, _Gwaiting, _Grunnable)
   243  		if trace.ok() {
   244  			trace.GoUnpark(callingG, 0)
   245  			traceRelease(trace)
   246  		}
   247  		execute(callingG, true)
   248  	})
   249  }
   250  
   251  func debugCallWrap2(dispatch uintptr) {
   252  	// Call the dispatch function and trap panics.
   253  	var dispatchF func()
   254  	dispatchFV := funcval{dispatch}
   255  	*(*unsafe.Pointer)(unsafe.Pointer(&dispatchF)) = noescape(unsafe.Pointer(&dispatchFV))
   256  
   257  	var ok bool
   258  	defer func() {
   259  		if !ok {
   260  			err := recover()
   261  			debugCallPanicked(err)
   262  		}
   263  	}()
   264  	dispatchF()
   265  	ok = true
   266  }
   267  

View as plain text