OSDN Git Service

runtime: Print stack trace on panic or signal.
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 17 May 2012 05:30:43 +0000 (05:30 +0000)
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 17 May 2012 05:30:43 +0000 (05:30 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gcc-4_7-branch@187624 138bc75d-0d04-0410-961f-82ee72b054a4

libgo/Makefile.am
libgo/Makefile.in
libgo/runtime/go-signal.c
libgo/runtime/go-traceback.c [new file with mode: 0644]
libgo/runtime/mprof.goc
libgo/runtime/proc.c
libgo/runtime/runtime.c
libgo/runtime/runtime.h

index f48a42c..a27216a 100644 (file)
@@ -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 \
index d7a2de6..018ac85 100644 (file)
@@ -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
index abe78c4..5d398b0 100644 (file)
@@ -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 (file)
index 0000000..2ff2ce3
--- /dev/null
@@ -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);
+}
index c61c65c..a8bee20 100644 (file)
@@ -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;
index 0862c60..890b492 100644 (file)
@@ -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<<mcpuShift));
-       if(m->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);
 }
 
index c8443cb..3c8d762 100644 (file)
 
 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);
+}
index 3e75b15..9104418 100644 (file)
@@ -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 *);