From: Ian Lance Taylor Date: Thu, 17 May 2012 05:30:25 +0000 (+0000) Subject: runtime: Print stack trace on panic or signal. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=d7b8f2b7816952c96c769c62eca10c647caa3530;p=gcc.git runtime: Print stack trace on panic or signal. From-SVN: r187623 --- diff --git a/libgo/Makefile.am b/libgo/Makefile.am index f48a42cbc12..a27216a9626 100644 --- a/libgo/Makefile.am +++ b/libgo/Makefile.am @@ -439,6 +439,7 @@ runtime_files = \ runtime/go-string-to-int-array.c \ runtime/go-strplus.c \ runtime/go-strslice.c \ + runtime/go-traceback.c \ runtime/go-trampoline.c \ runtime/go-type-complex.c \ runtime/go-type-eface.c \ diff --git a/libgo/Makefile.in b/libgo/Makefile.in index d7a2de65e00..018ac859fcb 100644 --- a/libgo/Makefile.in +++ b/libgo/Makefile.in @@ -197,22 +197,23 @@ am__libgo_la_SOURCES_DIST = runtime/go-append.c runtime/go-assert.c \ runtime/go-setenv.c runtime/go-signal.c runtime/go-strcmp.c \ runtime/go-string-to-byte-array.c \ runtime/go-string-to-int-array.c runtime/go-strplus.c \ - runtime/go-strslice.c runtime/go-trampoline.c \ - runtime/go-type-complex.c runtime/go-type-eface.c \ - runtime/go-type-error.c runtime/go-type-float.c \ - runtime/go-type-identity.c runtime/go-type-interface.c \ - runtime/go-type-string.c runtime/go-typedesc-equal.c \ - runtime/go-typestring.c runtime/go-unsafe-new.c \ - runtime/go-unsafe-newarray.c runtime/go-unsafe-pointer.c \ - runtime/go-unwind.c runtime/chan.c runtime/cpuprof.c \ - runtime/lock_sema.c runtime/thread-sema.c runtime/lock_futex.c \ - runtime/thread-linux.c runtime/mcache.c runtime/mcentral.c \ - runtime/mem_posix_memalign.c runtime/mem.c runtime/mfinal.c \ - runtime/mfixalloc.c runtime/mgc0.c runtime/mheap.c \ - runtime/msize.c runtime/proc.c runtime/runtime.c \ - runtime/signal_unix.c runtime/thread.c runtime/yield.c \ - runtime/rtems-task-variable-add.c iface.c malloc.c map.c \ - mprof.c reflect.c runtime1.c sema.c sigqueue.c string.c time.c + runtime/go-strslice.c runtime/go-traceback.c \ + runtime/go-trampoline.c runtime/go-type-complex.c \ + runtime/go-type-eface.c runtime/go-type-error.c \ + runtime/go-type-float.c runtime/go-type-identity.c \ + runtime/go-type-interface.c runtime/go-type-string.c \ + runtime/go-typedesc-equal.c runtime/go-typestring.c \ + runtime/go-unsafe-new.c runtime/go-unsafe-newarray.c \ + runtime/go-unsafe-pointer.c runtime/go-unwind.c runtime/chan.c \ + runtime/cpuprof.c runtime/lock_sema.c runtime/thread-sema.c \ + runtime/lock_futex.c runtime/thread-linux.c runtime/mcache.c \ + runtime/mcentral.c runtime/mem_posix_memalign.c runtime/mem.c \ + runtime/mfinal.c runtime/mfixalloc.c runtime/mgc0.c \ + runtime/mheap.c runtime/msize.c runtime/proc.c \ + runtime/runtime.c runtime/signal_unix.c runtime/thread.c \ + runtime/yield.c runtime/rtems-task-variable-add.c iface.c \ + malloc.c map.c mprof.c reflect.c runtime1.c sema.c sigqueue.c \ + string.c time.c @LIBGO_IS_LINUX_FALSE@am__objects_1 = lock_sema.lo thread-sema.lo @LIBGO_IS_LINUX_TRUE@am__objects_1 = lock_futex.lo thread-linux.lo @HAVE_SYS_MMAN_H_FALSE@am__objects_2 = mem_posix_memalign.lo @@ -234,16 +235,16 @@ am__objects_4 = go-append.lo go-assert.lo go-assert-interface.lo \ go-reflect-map.lo go-rune.lo go-runtime-error.lo go-setenv.lo \ go-signal.lo go-strcmp.lo go-string-to-byte-array.lo \ go-string-to-int-array.lo go-strplus.lo go-strslice.lo \ - go-trampoline.lo go-type-complex.lo go-type-eface.lo \ - go-type-error.lo go-type-float.lo go-type-identity.lo \ - go-type-interface.lo go-type-string.lo go-typedesc-equal.lo \ - go-typestring.lo go-unsafe-new.lo go-unsafe-newarray.lo \ - go-unsafe-pointer.lo go-unwind.lo chan.lo cpuprof.lo \ - $(am__objects_1) mcache.lo mcentral.lo $(am__objects_2) \ - mfinal.lo mfixalloc.lo mgc0.lo mheap.lo msize.lo proc.lo \ - runtime.lo signal_unix.lo thread.lo yield.lo $(am__objects_3) \ - iface.lo malloc.lo map.lo mprof.lo reflect.lo runtime1.lo \ - sema.lo sigqueue.lo string.lo time.lo + go-traceback.lo go-trampoline.lo go-type-complex.lo \ + go-type-eface.lo go-type-error.lo go-type-float.lo \ + go-type-identity.lo go-type-interface.lo go-type-string.lo \ + go-typedesc-equal.lo go-typestring.lo go-unsafe-new.lo \ + go-unsafe-newarray.lo go-unsafe-pointer.lo go-unwind.lo \ + chan.lo cpuprof.lo $(am__objects_1) mcache.lo mcentral.lo \ + $(am__objects_2) mfinal.lo mfixalloc.lo mgc0.lo mheap.lo \ + msize.lo proc.lo runtime.lo signal_unix.lo thread.lo yield.lo \ + $(am__objects_3) iface.lo malloc.lo map.lo mprof.lo reflect.lo \ + runtime1.lo sema.lo sigqueue.lo string.lo time.lo am_libgo_la_OBJECTS = $(am__objects_4) libgo_la_OBJECTS = $(am_libgo_la_OBJECTS) libgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ @@ -855,6 +856,7 @@ runtime_files = \ runtime/go-string-to-int-array.c \ runtime/go-strplus.c \ runtime/go-strslice.c \ + runtime/go-traceback.c \ runtime/go-trampoline.c \ runtime/go-type-complex.c \ runtime/go-type-eface.c \ @@ -2408,6 +2410,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-string-to-int-array.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-strplus.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-strslice.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-traceback.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-trampoline.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-complex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-eface.Plo@am__quote@ @@ -2823,6 +2826,13 @@ go-strslice.lo: runtime/go-strslice.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-strslice.lo `test -f 'runtime/go-strslice.c' || echo '$(srcdir)/'`runtime/go-strslice.c +go-traceback.lo: runtime/go-traceback.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-traceback.lo -MD -MP -MF $(DEPDIR)/go-traceback.Tpo -c -o go-traceback.lo `test -f 'runtime/go-traceback.c' || echo '$(srcdir)/'`runtime/go-traceback.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-traceback.Tpo $(DEPDIR)/go-traceback.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-traceback.c' object='go-traceback.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-traceback.lo `test -f 'runtime/go-traceback.c' || echo '$(srcdir)/'`runtime/go-traceback.c + go-trampoline.lo: runtime/go-trampoline.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-trampoline.lo -MD -MP -MF $(DEPDIR)/go-trampoline.Tpo -c -o go-trampoline.lo `test -f 'runtime/go-trampoline.c' || echo '$(srcdir)/'`runtime/go-trampoline.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-trampoline.Tpo $(DEPDIR)/go-trampoline.Plo diff --git a/libgo/runtime/go-signal.c b/libgo/runtime/go-signal.c index abe78c44ea8..5d398b04a02 100644 --- a/libgo/runtime/go-signal.c +++ b/libgo/runtime/go-signal.c @@ -157,7 +157,6 @@ sig_handler (int sig) for (i = 0; runtime_sigtab[i].sig != -1; ++i) { - struct sigaction sa; SigTab *t; t = &runtime_sigtab[i]; @@ -177,21 +176,33 @@ sig_handler (int sig) runtime_startpanic (); - /* We should do a stack backtrace here. Until we can do that, - we reraise the signal in order to get a slightly better - report from the shell. */ + { + const char *name = NULL; - memset (&sa, 0, sizeof sa); +#ifdef HAVE_STRSIGNAL + name = strsignal (sig); +#endif - sa.sa_handler = SIG_DFL; + if (name == NULL) + runtime_printf ("Signal %d\n", sig); + else + runtime_printf ("%s\n", name); + } - i = sigemptyset (&sa.sa_mask); - __go_assert (i == 0); + runtime_printf ("\n"); - if (sigaction (sig, &sa, NULL) != 0) - abort (); + if (runtime_gotraceback ()) + { + G *g; - raise (sig); + g = runtime_g (); + runtime_traceback (g); + runtime_tracebackothers (g); + + /* The gc library calls runtime_dumpregs here, and provides + a function that prints the registers saved in context in + a readable form. */ + } runtime_exit (2); } @@ -230,12 +241,22 @@ static void sig_panic_info_handler (int sig, siginfo_t *info, void *context __attribute__ ((unused))) { - if (runtime_g () == NULL || info->si_code == SI_USER) + G *g; + + g = runtime_g (); + if (g == NULL || info->si_code == SI_USER) { sig_handler (sig); return; } + g->sig = sig; + g->sigcode0 = info->si_code; + g->sigcode1 = (uintptr_t) info->si_addr; + + /* It would be nice to set g->sigpc here as the gc library does, but + I don't know how to get it portably. */ + sig_panic_leadin (sig); switch (sig) @@ -284,12 +305,19 @@ sig_panic_info_handler (int sig, siginfo_t *info, static void sig_panic_handler (int sig) { - if (runtime_g () == NULL) + G *g; + + g = runtime_g (); + if (g == NULL) { sig_handler (sig); return; } + g->sig = sig; + g->sigcode0 = 0; + g->sigcode1 = 0; + sig_panic_leadin (sig); switch (sig) diff --git a/libgo/runtime/go-traceback.c b/libgo/runtime/go-traceback.c new file mode 100644 index 00000000000..2ff2ce3faef --- /dev/null +++ b/libgo/runtime/go-traceback.c @@ -0,0 +1,62 @@ +/* go-traceback.c -- stack backtrace for Go. + + Copyright 2012 The Go Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. */ + +#include "config.h" + +#include "unwind.h" + +#include "runtime.h" +#include "go-string.h" + +static _Unwind_Reason_Code +traceback (struct _Unwind_Context *context, void *varg) +{ + int *parg = (int *) varg; + uintptr pc; + int ip_before_insn = 0; + struct __go_string fn; + struct __go_string file; + int line; + +#ifdef HAVE_GETIPINFO + pc = _Unwind_GetIPInfo (context, &ip_before_insn); +#else + pc = _Unwind_GetIP (context); +#endif + + if (*parg > 100) + return _URC_END_OF_STACK; + ++*parg; + + /* FIXME: If PC is in the __morestack routine, we should ignore + it. */ + + /* Back up to the call instruction. */ + if (!ip_before_insn) + --pc; + + if (!__go_file_line (pc, &fn, &file, &line)) + return _URC_END_OF_STACK; + + if (runtime_showframe (fn.__data)) + { + runtime_printf ("%s\n", fn.__data); + runtime_printf ("\t%s:%d\n", file.__data, line); + } + + return _URC_NO_REASON; +} + +/* Print a stack trace for the current goroutine. */ + +void +runtime_traceback () +{ + int c; + + c = 0; + _Unwind_Backtrace (traceback, &c); +} diff --git a/libgo/runtime/mprof.goc b/libgo/runtime/mprof.goc index c61c65ccee3..a8bee206f9c 100644 --- a/libgo/runtime/mprof.goc +++ b/libgo/runtime/mprof.goc @@ -361,10 +361,11 @@ func Stack(b Slice, all bool) (n int32) { g->writenbuf = b.__count; USED(pc); USED(sp); - // runtime_goroutineheader(g); - // runtime_traceback(pc, sp, 0, g); - // if(all) - // runtime_tracebackothers(g); + runtime_goroutineheader(g); + runtime_traceback(); + runtime_goroutinetrailer(g); + if(all) + runtime_tracebackothers(g); n = b.__count - g->writenbuf; g->writebuf = nil; g->writenbuf = 0; diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c index 0862c604640..890b492d404 100644 --- a/libgo/runtime/proc.c +++ b/libgo/runtime/proc.c @@ -50,6 +50,8 @@ uintptr runtime_stacks_sys; static void schedule(G*); +static void gtraceback(G*); + typedef struct Sched Sched; M runtime_m0; @@ -345,6 +347,9 @@ runtime_mcall(void (*pfn)(G*)) // the values for this thread. mp = runtime_m(); gp = runtime_g(); + + if(gp->dotraceback != nil) + gtraceback(gp); } if (gp == nil || !gp->fromgogo) { #ifdef USING_SPLIT_STACK @@ -523,19 +528,73 @@ runtime_goroutineheader(G *g) } void -runtime_tracebackothers(G *me) +runtime_goroutinetrailer(G *g) { - G *g; + if(g != nil && g->gopc != 0 && g->goid != 1) { + struct __go_string fn; + struct __go_string file; + int line; + + if(__go_file_line(g->gopc - 1, &fn, &file, &line)) { + runtime_printf("created by %s\n", fn.__data); + runtime_printf("\t%s:%d\n", file.__data, line); + } + } +} + +void +runtime_tracebackothers(G * volatile me) +{ + G * volatile g; for(g = runtime_allg; g != nil; g = g->alllink) { if(g == me || g->status == Gdead) continue; runtime_printf("\n"); runtime_goroutineheader(g); - // runtime_traceback(g->sched.pc, g->sched.sp, 0, g); + + // Our only mechanism for doing a stack trace is + // _Unwind_Backtrace. And that only works for the + // current thread, not for other random goroutines. + // So we need to switch context to the goroutine, get + // the backtrace, and then switch back. + + // This means that if g is running or in a syscall, we + // can't reliably print a stack trace. FIXME. + if(g->status == Gsyscall || g->status == Grunning) { + runtime_printf("no stack trace available\n"); + runtime_goroutinetrailer(g); + continue; + } + + g->dotraceback = me; + +#ifdef USING_SPLIT_STACK + __splitstack_getcontext(&me->stack_context[0]); +#endif + getcontext(&me->context); + + if(g->dotraceback) { + runtime_gogo(g); + } } } +// Do a stack trace of gp, and then restore the context to +// gp->dotraceback. + +static void +gtraceback(G* gp) +{ + G* ret; + + runtime_traceback(nil); + runtime_goroutinetrailer(gp); + ret = gp->dotraceback; + gp->dotraceback = nil; + runtime_gogo(ret); +} + // Mark this g as m's idle goroutine. // This functionality might be used in environments where programs // are limited to a single thread, to simulate a select-driven @@ -1171,7 +1230,7 @@ runtime_entersyscall(void) // Leave SP around for gc and traceback. #ifdef USING_SPLIT_STACK - g->gcstack = __splitstack_find(NULL, NULL, &g->gcstack_size, + g->gcstack = __splitstack_find(nil, nil, &g->gcstack_size, &g->gcnext_segment, &g->gcnext_sp, &g->gcinitial_sp); #else @@ -1227,9 +1286,11 @@ runtime_exitsyscall(void) // find that we still have mcpu <= mcpumax, then we can // start executing Go code immediately, without having to // schedlock/schedunlock. + // Also do fast return if any locks are held, so that + // panic code can use syscalls to open a file. gp = g; v = runtime_xadd(&runtime_sched.atomic, (1<profilehz == runtime_sched.profilehz && atomic_mcpu(v) <= atomic_mcpumax(v)) { + if((m->profilehz == runtime_sched.profilehz && atomic_mcpu(v) <= atomic_mcpumax(v)) || m->locks > 0) { // There's a cpu for us, so we can run. gp->status = Grunning; // Garbage collector isn't running (since we are), @@ -1561,7 +1622,7 @@ runtime_sigprof(uint8 *pc __attribute__ ((unused)), uint8 *lr __attribute__ ((unused)), G *gp __attribute__ ((unused))) { - // int32 n; + int32 n; if(prof.fn == nil || prof.hz == 0) return; @@ -1571,9 +1632,9 @@ runtime_sigprof(uint8 *pc __attribute__ ((unused)), runtime_unlock(&prof); return; } - // n = runtime_gentraceback(pc, sp, lr, gp, 0, prof.pcbuf, nelem(prof.pcbuf)); - // if(n > 0) - // prof.fn(prof.pcbuf, n); + n = runtime_callers(0, prof.pcbuf, nelem(prof.pcbuf)); + if(n > 0) + prof.fn(prof.pcbuf, n); runtime_unlock(&prof); } diff --git a/libgo/runtime/runtime.c b/libgo/runtime/runtime.c index c8443cbed99..3c8d76225fc 100644 --- a/libgo/runtime/runtime.c +++ b/libgo/runtime/runtime.c @@ -11,6 +11,17 @@ uint32 runtime_panicking; +int32 +runtime_gotraceback(void) +{ + const byte *p; + + p = runtime_getenv("GOTRACEBACK"); + if(p == nil || p[0] == '\0') + return 1; // default is on + return runtime_atoi(p); +} + static Lock paniclk; void @@ -31,20 +42,26 @@ runtime_startpanic(void) void runtime_dopanic(int32 unused __attribute__ ((unused))) { - /* + G* g; static bool didothers; + g = runtime_g(); if(g->sig != 0) - runtime_printf("[signal %x code=%p addr=%p pc=%p]\n", - g->sig, g->sigcode0, g->sigcode1, g->sigpc); + runtime_printf("[signal %x code=%p addr=%p]\n", + g->sig, (void*)(g->sigcode0), (void*)(g->sigcode1)); if(runtime_gotraceback()){ + if(g != runtime_m()->g0) { + runtime_printf("\n"); + runtime_goroutineheader(g); + runtime_traceback(); + runtime_goroutinetrailer(g); + } if(!didothers) { didothers = true; runtime_tracebackothers(g); } } - */ runtime_unlock(&paniclk); if(runtime_xadd(&runtime_panicking, -1) != 0) { @@ -185,10 +202,10 @@ runtime_fastrand1(void) } static struct root_list runtime_roots = -{ NULL, +{ nil, { { &syscall_Envs, sizeof syscall_Envs }, { &os_Args, sizeof os_Args }, - { NULL, 0 } }, + { nil, 0 } }, }; void @@ -209,3 +226,13 @@ runtime_cputicks(void) return 0; #endif } + +bool +runtime_showframe(const unsigned char *s) +{ + static int32 traceback = -1; + + if(traceback < 0) + traceback = runtime_gotraceback(); + return traceback > 1 || (__builtin_strchr((const char*)s, '.') != nil && __builtin_memcmp(s, "runtime.", 7) != 0); +} diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h index 3e75b1583b8..91044184b1f 100644 --- a/libgo/runtime/runtime.h +++ b/libgo/runtime/runtime.h @@ -143,14 +143,16 @@ struct G M* m; // for debuggers, but offset not hard-coded M* lockedm; M* idlem; - // int32 sig; + int32 sig; int32 writenbuf; byte* writebuf; - // uintptr sigcode0; - // uintptr sigcode1; + uintptr sigcode0; + uintptr sigcode1; // uintptr sigpc; uintptr gopc; // pc of go statement that created this goroutine + G* dotraceback; + ucontext_t context; void* stack_context[10]; }; @@ -289,6 +291,11 @@ void* runtime_mal(uintptr); void runtime_schedinit(void); void runtime_initsig(void); void runtime_sigenable(uint32 sig); +int32 runtime_gotraceback(void); +void runtime_goroutineheader(G*); +void runtime_goroutinetrailer(G*); +void runtime_traceback(); +void runtime_tracebackothers(G*); String runtime_gostringnocopy(const byte*); void* runtime_mstart(void*); G* runtime_malg(int32, byte**, size_t*); @@ -434,6 +441,8 @@ void runtime_osyield(void); void runtime_LockOSThread(void) __asm__("runtime.LockOSThread"); void runtime_UnlockOSThread(void) __asm__("runtime.UnlockOSThread"); +bool runtime_showframe(const unsigned char*); + uintptr runtime_memlimit(void); // If appropriate, ask the operating system to control whether this @@ -468,3 +477,5 @@ void __go_register_gc_roots(struct root_list*); // This will be 0 when using split stacks, as in that case // the stacks are allocated by the splitstack library. extern uintptr runtime_stacks_sys; + +extern _Bool __go_file_line (uintptr, String*, String*, int *);