github.com/peggyl/go@v0.0.0-20151008231540-ae315999c2d5/src/runtime/signal_386.go (about)

     1  // Copyright 2013 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 darwin dragonfly freebsd linux nacl netbsd openbsd
     6  
     7  package runtime
     8  
     9  import "unsafe"
    10  
    11  func dumpregs(c *sigctxt) {
    12  	print("eax    ", hex(c.eax()), "\n")
    13  	print("ebx    ", hex(c.ebx()), "\n")
    14  	print("ecx    ", hex(c.ecx()), "\n")
    15  	print("edx    ", hex(c.edx()), "\n")
    16  	print("edi    ", hex(c.edi()), "\n")
    17  	print("esi    ", hex(c.esi()), "\n")
    18  	print("ebp    ", hex(c.ebp()), "\n")
    19  	print("esp    ", hex(c.esp()), "\n")
    20  	print("eip    ", hex(c.eip()), "\n")
    21  	print("eflags ", hex(c.eflags()), "\n")
    22  	print("cs     ", hex(c.cs()), "\n")
    23  	print("fs     ", hex(c.fs()), "\n")
    24  	print("gs     ", hex(c.gs()), "\n")
    25  }
    26  
    27  var crashing int32
    28  
    29  // May run during STW, so write barriers are not allowed.
    30  //go:nowritebarrier
    31  func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
    32  	_g_ := getg()
    33  	c := &sigctxt{info, ctxt}
    34  
    35  	if sig == _SIGPROF {
    36  		sigprof(uintptr(c.eip()), uintptr(c.esp()), 0, gp, _g_.m)
    37  		return
    38  	}
    39  
    40  	flags := int32(_SigThrow)
    41  	if sig < uint32(len(sigtable)) {
    42  		flags = sigtable[sig].flags
    43  	}
    44  	if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
    45  		// Make it look like a call to the signal func.
    46  		// Have to pass arguments out of band since
    47  		// augmenting the stack frame would break
    48  		// the unwinding code.
    49  		gp.sig = sig
    50  		gp.sigcode0 = uintptr(c.sigcode())
    51  		gp.sigcode1 = uintptr(c.sigaddr())
    52  		gp.sigpc = uintptr(c.eip())
    53  
    54  		if GOOS == "darwin" {
    55  			// Work around Leopard bug that doesn't set FPE_INTDIV.
    56  			// Look at instruction to see if it is a divide.
    57  			// Not necessary in Snow Leopard (si_code will be != 0).
    58  			if sig == _SIGFPE && gp.sigcode0 == 0 {
    59  				pc := (*[4]byte)(unsafe.Pointer(gp.sigpc))
    60  				i := 0
    61  				if pc[i] == 0x66 { // 16-bit instruction prefix
    62  					i++
    63  				}
    64  				if pc[i] == 0xF6 || pc[i] == 0xF7 {
    65  					gp.sigcode0 = _FPE_INTDIV
    66  				}
    67  			}
    68  		}
    69  
    70  		pc := uintptr(c.eip())
    71  		sp := uintptr(c.esp())
    72  
    73  		// If we don't recognize the PC as code
    74  		// but we do recognize the top pointer on the stack as code,
    75  		// then assume this was a call to non-code and treat like
    76  		// pc == 0, to make unwinding show the context.
    77  		if pc != 0 && findfunc(pc) == nil && findfunc(*(*uintptr)(unsafe.Pointer(sp))) != nil {
    78  			pc = 0
    79  		}
    80  
    81  		// Only push runtime.sigpanic if pc != 0.
    82  		// If pc == 0, probably panicked because of a
    83  		// call to a nil func.  Not pushing that onto sp will
    84  		// make the trace look like a call to runtime.sigpanic instead.
    85  		// (Otherwise the trace will end at runtime.sigpanic and we
    86  		// won't get to see who faulted.)
    87  		if pc != 0 {
    88  			if regSize > ptrSize {
    89  				sp -= ptrSize
    90  				*(*uintptr)(unsafe.Pointer(sp)) = 0
    91  			}
    92  			sp -= ptrSize
    93  			*(*uintptr)(unsafe.Pointer(sp)) = pc
    94  			c.set_esp(uint32(sp))
    95  		}
    96  		c.set_eip(uint32(funcPC(sigpanic)))
    97  		return
    98  	}
    99  
   100  	if c.sigcode() == _SI_USER || flags&_SigNotify != 0 {
   101  		if sigsend(sig) {
   102  			return
   103  		}
   104  	}
   105  
   106  	if flags&_SigKill != 0 {
   107  		exit(2)
   108  	}
   109  
   110  	if flags&_SigThrow == 0 {
   111  		return
   112  	}
   113  
   114  	_g_.m.throwing = 1
   115  	_g_.m.caughtsig.set(gp)
   116  
   117  	if crashing == 0 {
   118  		startpanic()
   119  	}
   120  
   121  	if sig < uint32(len(sigtable)) {
   122  		print(sigtable[sig].name, "\n")
   123  	} else {
   124  		print("Signal ", sig, "\n")
   125  	}
   126  
   127  	print("PC=", hex(c.eip()), " m=", _g_.m.id, "\n")
   128  	if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
   129  		print("signal arrived during cgo execution\n")
   130  		gp = _g_.m.lockedg
   131  	}
   132  	print("\n")
   133  
   134  	var docrash bool
   135  	if gotraceback(&docrash) > 0 {
   136  		goroutineheader(gp)
   137  
   138  		// On Linux/386, all system calls go through the vdso kernel_vsyscall routine.
   139  		// Normally we don't see those PCs, but during signals we can.
   140  		// If we see a PC in the vsyscall area (it moves around, but near the top of memory),
   141  		// assume we're blocked in the vsyscall routine, which has saved
   142  		// three words on the stack after the initial call saved the caller PC.
   143  		// Pop all four words off SP and use the saved PC.
   144  		// The check of the stack bounds here should suffice to avoid a fault
   145  		// during the actual PC pop.
   146  		// If we do load a bogus PC, not much harm done: we weren't going
   147  		// to get a decent traceback anyway.
   148  		// TODO(rsc): Make this more precise: we should do more checks on the PC,
   149  		// and we should find out whether different versions of the vdso page
   150  		// use different prologues that store different amounts on the stack.
   151  		pc := uintptr(c.eip())
   152  		sp := uintptr(c.esp())
   153  		if GOOS == "linux" && pc >= 0xf4000000 && gp.stack.lo <= sp && sp+16 <= gp.stack.hi {
   154  			// Assume in vsyscall page.
   155  			sp += 16
   156  			pc = *(*uintptr)(unsafe.Pointer(sp - 4))
   157  			print("runtime: unwind vdso kernel_vsyscall: pc=", hex(pc), " sp=", hex(sp), "\n")
   158  		}
   159  
   160  		tracebacktrap(pc, sp, 0, gp)
   161  		if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
   162  			// tracebackothers on original m skipped this one; trace it now.
   163  			goroutineheader(_g_.m.curg)
   164  			traceback(^uintptr(0), ^uintptr(0), 0, gp)
   165  		} else if crashing == 0 {
   166  			tracebackothers(gp)
   167  			print("\n")
   168  		}
   169  		dumpregs(c)
   170  	}
   171  
   172  	if docrash {
   173  		crashing++
   174  		if crashing < sched.mcount {
   175  			// There are other m's that need to dump their stacks.
   176  			// Relay SIGQUIT to the next m by sending it to the current process.
   177  			// All m's that have already received SIGQUIT have signal masks blocking
   178  			// receipt of any signals, so the SIGQUIT will go to an m that hasn't seen it yet.
   179  			// When the last m receives the SIGQUIT, it will fall through to the call to
   180  			// crash below. Just in case the relaying gets botched, each m involved in
   181  			// the relay sleeps for 5 seconds and then does the crash/exit itself.
   182  			// In expected operation, the last m has received the SIGQUIT and run
   183  			// crash/exit and the process is gone, all long before any of the
   184  			// 5-second sleeps have finished.
   185  			print("\n-----\n\n")
   186  			raiseproc(_SIGQUIT)
   187  			usleep(5 * 1000 * 1000)
   188  		}
   189  		crash()
   190  	}
   191  
   192  	exit(2)
   193  }