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

View as plain text