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

View as plain text