Black Lives Matter. Support the Equal Justice Initiative.

Source file src/runtime/debugcall.go

Documentation: runtime

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

View as plain text