742 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			742 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*  $Id$
 | |
| 
 | |
|     Part of SWI-Prolog
 | |
| 
 | |
|     Author:        Jan Wielemaker
 | |
|     E-mail:        J.Wielemaker@uva.nl
 | |
|     WWW:           http://www.swi-prolog.org
 | |
|     Copyright (C): 1985-2011, University of Amsterdam
 | |
| 
 | |
|     This library is free software; you can redistribute it and/or
 | |
|     modify it under the terms of the GNU Lesser General Public
 | |
|     License as published by the Free Software Foundation; either
 | |
|     version 2.1 of the License, or (at your option) any later version.
 | |
| 
 | |
|     This library is distributed in the hope that it will be useful,
 | |
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|     Lesser General Public License for more details.
 | |
| 
 | |
|     You should have received a copy of the GNU Lesser General Public
 | |
|     License along with this library; if not, write to the Free Software
 | |
|     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 | |
| */
 | |
| 
 | |
| #ifdef _WIN64
 | |
| #define _WIN32_WINNT 0x0501            /* get RtlCaptureContext() */
 | |
| #endif
 | |
| 
 | |
| #include "pl-incl.h"
 | |
| #include "os/pl-cstack.h"
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| The task of the library is to save   the  <N> most recent C stack traces
 | |
| for later retrieval. I.e., although this library   can  be used to print
 | |
| the stack in case of a crash, it is   intended  to _save_ the stack on a
 | |
| critical event such as GC and retrieve it  later if it turns out that an
 | |
| error occurs.
 | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| #define SAVE_TRACES 10
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	      LIBUNWIND		*
 | |
| 		 *******************************/
 | |
| 
 | |
| #if !defined(BTRACE_DONE) && defined(HAVE_LIBUNWIND)
 | |
| #define BTRACE_DONE 1
 | |
| #define UNW_LOCAL_ONLY
 | |
| #include <libunwind.h>
 | |
| 
 | |
| #define MAX_DEPTH 10
 | |
| 
 | |
| typedef struct
 | |
| { char name[32];				/* function called */
 | |
|   unw_word_t offset;				/* offset in function */
 | |
| } frame_info;
 | |
| 
 | |
| typedef struct
 | |
| { const char *name;				/* label of the backtrace */
 | |
|   int depth;					/* # frames collectec */
 | |
|   frame_info frame[MAX_DEPTH];			/* per-frame info */
 | |
| } btrace_stack;
 | |
| 
 | |
| typedef struct btrace
 | |
| { btrace_stack dumps[SAVE_TRACES];		/* ring of buffers */
 | |
|   int current;					/* next to fill */
 | |
| } btrace;
 | |
| 
 | |
| 
 | |
| void
 | |
| btrace_destroy(struct btrace *bt)
 | |
| { free(bt);
 | |
| }
 | |
| 
 | |
| 
 | |
| static btrace *
 | |
| get_trace_store(void)
 | |
| { GET_LD
 | |
| 
 | |
|   if ( !LD->btrace_store )
 | |
|   { btrace *s = malloc(sizeof(*s));
 | |
|     if ( s )
 | |
|     { memset(s, 0, sizeof(*s));
 | |
|       LD->btrace_store = s;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return LD->btrace_store;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| next_btrace_id() produces the  id  for  the   next  backtrace  and  sets
 | |
| bt->current to the subsequent id. Although bt is thread-local, it may be
 | |
| called from a signal  handler  or   (Windows)  exception.  We cannot use
 | |
| locking because the mutex functions are not   async  signal safe. So, we
 | |
| use atomic instructions if possible. Otherwise, we ensure consistency of
 | |
| the datastructures, but we may overwrite an older stack trace.
 | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| static int
 | |
| next_btrace_id(btrace *bt)
 | |
| { int current;
 | |
| #ifdef COMPARE_AND_SWAP
 | |
|   int next;
 | |
| 
 | |
|   do
 | |
|   { current = bt->current;
 | |
|     next = current+1;
 | |
|     if ( next == SAVE_TRACES )
 | |
|       next = 0;
 | |
|   } while ( !COMPARE_AND_SWAP(&bt->current, current, next) );
 | |
| #else
 | |
|   current = bt->current++ % SAVE_TRACES;
 | |
| 
 | |
|   if ( bt->current >= SAVE_TRACES )
 | |
|     bt->current %= SAVE_TRACES;
 | |
| #endif
 | |
| 
 | |
|   return current;
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| save_backtrace(const char *why)
 | |
| { btrace *bt = get_trace_store();
 | |
| 
 | |
|   if ( bt )
 | |
|   { btrace_stack *s;
 | |
|     unw_cursor_t cursor; unw_context_t uc;
 | |
|     int depth;
 | |
|     int current = next_btrace_id(bt);
 | |
| 
 | |
|     s = &bt->dumps[current];
 | |
|     unw_getcontext(&uc);
 | |
|     unw_init_local(&cursor, &uc);
 | |
|     for(depth=0; unw_step(&cursor) > 0 && depth < MAX_DEPTH; depth++)
 | |
|     { unw_get_proc_name(&cursor,
 | |
| 			s->frame[depth].name, sizeof(s->frame[depth].name),
 | |
| 			&s->frame[depth].offset);
 | |
|     }
 | |
|     s->name = why;
 | |
|     s->depth = depth;
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| print_trace(btrace *bt, int me)
 | |
| { btrace_stack *s = &bt->dumps[me];
 | |
| 
 | |
|   if ( s && s->name )
 | |
|   { int depth;
 | |
| 
 | |
|     Sdprintf("Stack trace labeled \"%s\":\n", s->name);
 | |
|     for(depth=0; depth<s->depth; depth++)
 | |
|     { Sdprintf("  [%d] %s+%p\n", depth,
 | |
| 	       s->frame[depth].name,
 | |
| 	       (void*)s->frame[depth].offset);
 | |
|     }
 | |
|   } else
 | |
|   { Sdprintf("No stack trace\n");
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| print_backtrace(int last)		/* 1..SAVE_TRACES */
 | |
| { btrace *bt = get_trace_store();
 | |
| 
 | |
|   if ( bt )
 | |
|   { int me = bt->current-last;
 | |
|     if ( me < 0 )
 | |
|       me += SAVE_TRACES;
 | |
| 
 | |
|     print_trace(bt, me);
 | |
|   } else
 | |
|   { Sdprintf("No backtrace store?\n");
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| print_backtrace_named(const char *why)
 | |
| { btrace *bt = get_trace_store();
 | |
| 
 | |
|   if ( bt )
 | |
|   { int me = bt->current-1;
 | |
| 
 | |
|     for(;;)
 | |
|     { if ( --me < 0 )
 | |
| 	me += SAVE_TRACES;
 | |
|       if ( bt->dumps[me].name && strcmp(bt->dumps[me].name, why) == 0 )
 | |
|       { print_trace(bt, me);
 | |
| 	return;
 | |
|       }
 | |
|       if ( me == bt->current-1 )
 | |
| 	break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Sdprintf("No backtrace named %s\n", why);
 | |
| }
 | |
| 
 | |
| #endif /*HAVE_LIBUNWIND*/
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	       GLIBC		*
 | |
| 		 *******************************/
 | |
| 
 | |
| #if !defined(BTRACE_DONE) && defined(HAVE_EXECINFO_H) && !defined(DMALLOC)
 | |
| #define BTRACE_DONE 1
 | |
| #include <execinfo.h>
 | |
| #include <string.h>
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | |
| This implementation uses the libgcc unwinding capabilities.
 | |
| 
 | |
| Disabled of dmalloc is used because the  free of the memory allocated by
 | |
| backtrace_symbols() is considered an error by dmalloc.
 | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| typedef struct btrace
 | |
| { char	      **symbols[SAVE_TRACES];
 | |
|   const char   *why[SAVE_TRACES];
 | |
|   size_t	sizes[SAVE_TRACES];
 | |
|   int		current;
 | |
| } btrace;
 | |
| 
 | |
| 
 | |
| void
 | |
| btrace_destroy(struct btrace *bt)
 | |
| { int i;
 | |
| 
 | |
|   for(i=0; i<SAVE_TRACES; i++)
 | |
|   { if ( bt->symbols[i] )
 | |
|       free(bt->symbols[i]);
 | |
|   }
 | |
| 
 | |
|   free(bt);
 | |
| }
 | |
| 
 | |
| 
 | |
| static btrace *
 | |
| get_trace_store(void)
 | |
| { GET_LD
 | |
| 
 | |
|   if ( !LD->btrace_store )
 | |
|   { btrace *s = malloc(sizeof(*s));
 | |
|     if ( s )
 | |
|     { memset(s, 0, sizeof(*s));
 | |
|       LD->btrace_store = s;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return LD->btrace_store;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Copy of same function above.  Relies on a different btrace structure.
 | |
|    Ideally, this should be shared :-(
 | |
| */
 | |
| 
 | |
| static int
 | |
| next_btrace_id(btrace *bt)
 | |
| { int current;
 | |
| #ifdef COMPARE_AND_SWAP
 | |
|   int next;
 | |
| 
 | |
|   do
 | |
|   { current = bt->current;
 | |
|     next = current+1;
 | |
|     if ( next == SAVE_TRACES )
 | |
|       next = 0;
 | |
|   } while ( !COMPARE_AND_SWAP(&bt->current, current, next) );
 | |
| #else
 | |
|   current = bt->current++ % SAVE_TRACES;
 | |
| 
 | |
|   if ( bt->current >= SAVE_TRACES )
 | |
|     bt->current %= SAVE_TRACES;
 | |
| #endif
 | |
| 
 | |
|   return current;
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| save_backtrace(const char *why)
 | |
| { btrace *bt = get_trace_store();
 | |
| 
 | |
|   if ( bt )
 | |
|   { void *array[100];
 | |
|     size_t frames;
 | |
|     int current = next_btrace_id(bt);
 | |
| 
 | |
|     frames = backtrace(array, sizeof(array)/sizeof(void *));
 | |
|     bt->sizes[current] = frames;
 | |
|     if ( bt->symbols[current] )
 | |
|       free(bt->symbols[current]);
 | |
|     bt->symbols[current] = backtrace_symbols(array, frames);
 | |
|     bt->why[current] = why;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| print_trace(btrace *bt, int me)
 | |
| { size_t i;
 | |
| 
 | |
|   if ( bt->why[me] )
 | |
|   { Sdprintf("Stack trace labeled \"%s\":\n", bt->why[me]);
 | |
| 
 | |
|     for(i=0; i<bt->sizes[me]; i++)
 | |
|       Sdprintf("  [%d] %s\n", i, bt->symbols[me][i]);
 | |
|   } else
 | |
|   { Sdprintf("No stack trace\n");
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| print_backtrace(int last)		/* 1..SAVE_TRACES */
 | |
| { btrace *bt = get_trace_store();
 | |
| 
 | |
|   if ( bt )
 | |
|   { int me = bt->current-last;
 | |
|     if ( me < 0 )
 | |
|       me += SAVE_TRACES;
 | |
| 
 | |
|     print_trace(bt, me);
 | |
|   } else
 | |
|   { Sdprintf("No backtrace store?\n");
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| print_backtrace_named(const char *why)
 | |
| { btrace *bt = get_trace_store();
 | |
| 
 | |
|   if ( bt )
 | |
|   { int me = bt->current-1;
 | |
| 
 | |
|     for(;;)
 | |
|     { if ( --me < 0 )
 | |
| 	me += SAVE_TRACES;
 | |
|       if ( bt->why[me] && strcmp(bt->why[me], why) == 0 )
 | |
|       { print_trace(bt, me);
 | |
| 	return;
 | |
|       }
 | |
|       if ( me == bt->current-1 )
 | |
| 	break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Sdprintf("No backtrace named %s\n", why);
 | |
| }
 | |
| 
 | |
| 
 | |
| #endif /*HAVE_EXECINFO_H*/
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *	  ADD AS HANDLER	*
 | |
| 		 *******************************/
 | |
| 
 | |
| #ifdef BTRACE_DONE
 | |
| 
 | |
| static void
 | |
| crashHandler(int sig)
 | |
| { Sdprintf("\nSWI-Prolog [thread %d]: received fatal signal %d (%s)\n",
 | |
| 	   PL_thread_self(), sig, signal_name(sig));
 | |
|   save_backtrace("crash");
 | |
|   print_backtrace_named("crash");
 | |
|   abort();
 | |
| }
 | |
| 
 | |
| void
 | |
| initBackTrace(void)
 | |
| {
 | |
| #ifdef SIGSEGV
 | |
|   PL_signal(SIGSEGV, crashHandler);
 | |
| #endif
 | |
| #ifdef SIGILL
 | |
|   PL_signal(SIGILL, crashHandler);
 | |
| #endif
 | |
| #ifdef SIGBUS
 | |
|   PL_signal(SIGBUS, crashHandler);
 | |
| #endif
 | |
| #ifdef SIGFPE
 | |
|   PL_signal(SIGFPE, crashHandler);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| 
 | |
| 		 /*******************************
 | |
| 		 *   WINDOWS IMPLEMENTATION	    *
 | |
| 		 *******************************/
 | |
| 
 | |
| 
 | |
| #if !defined(BTRACE_DONE) && defined(__WINDOWS__) && defined(HAVE_DBGHELP_H)
 | |
| #include <windows.h>
 | |
| #include <dbghelp.h>
 | |
| #define MAX_SYMBOL_LEN 1024
 | |
| #define MAX_DEPTH 10
 | |
| #define BTRACE_DONE 1
 | |
| 
 | |
| #define MAX_FUNCTION_NAME_LENGTH 32
 | |
| /* Note that the module name may include the full path in some versions
 | |
|    of dbghelp. For me, 32 was not enough to see the module name in some
 | |
|    cases.
 | |
| */
 | |
| #define MAX_MODULE_NAME_LENGTH 64
 | |
| 
 | |
| #define LOCK()   PL_LOCK(L_CSTACK)
 | |
| #define UNLOCK() PL_UNLOCK(L_CSTACK)
 | |
| 
 | |
| typedef struct
 | |
| { char name[MAX_FUNCTION_NAME_LENGTH];	/* function called */
 | |
|   DWORD64 offset;			/* offset in function */
 | |
|   char module[MAX_MODULE_NAME_LENGTH];	/* module of function */
 | |
|   DWORD module_reason;                  /* reason for module being absent */
 | |
| } frame_info;
 | |
| 
 | |
| typedef struct
 | |
| { const char *name;			/* label of the backtrace */
 | |
|   int depth;				/* # frames collectec */
 | |
|   frame_info frame[MAX_DEPTH];		/* per-frame info */
 | |
| } btrace_stack;
 | |
| 
 | |
| typedef struct btrace
 | |
| { btrace_stack dumps[SAVE_TRACES];	/* ring of buffers */
 | |
|   int current;				/* next to fill */
 | |
| } btrace;
 | |
| 
 | |
| void
 | |
| btrace_destroy(struct btrace *bt)
 | |
| { free(bt);
 | |
| }
 | |
| 
 | |
| 
 | |
| static btrace *
 | |
| get_trace_store(void)
 | |
| { GET_LD
 | |
| 
 | |
|   if ( !LD->btrace_store )
 | |
|   { btrace *s = malloc(sizeof(*s));
 | |
|     if ( s )
 | |
|     { memset(s, 0, sizeof(*s));
 | |
|       LD->btrace_store = s;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return LD->btrace_store;
 | |
| }
 | |
| 
 | |
| /* Copy of same function above.  Relies on a different btrace structure.
 | |
|    Ideally, this should be shared :-(
 | |
| */
 | |
| 
 | |
| static int
 | |
| next_btrace_id(btrace *bt)
 | |
| { int current;
 | |
| #ifdef COMPARE_AND_SWAP
 | |
|   int next;
 | |
| 
 | |
|   do
 | |
|   { current = bt->current;
 | |
|     next = current+1;
 | |
|     if ( next == SAVE_TRACES )
 | |
|       next = 0;
 | |
|   } while ( !COMPARE_AND_SWAP(&bt->current, current, next) );
 | |
| #else
 | |
|   current = bt->current++ % SAVE_TRACES;
 | |
| 
 | |
|   if ( bt->current >= SAVE_TRACES )
 | |
|     bt->current %= SAVE_TRACES;
 | |
| #endif
 | |
| 
 | |
|   return current;
 | |
| }
 | |
| 
 | |
| int backtrace(btrace_stack* trace, PEXCEPTION_POINTERS pExceptionInfo)
 | |
| { STACKFRAME64 frame;
 | |
|   CONTEXT context;
 | |
|   int rc = 0;
 | |
|   HANDLE hThread = GetCurrentThread();
 | |
|   HANDLE hProcess = GetCurrentProcess();
 | |
|   char symbolScratch[sizeof(SYMBOL_INFO) + MAX_SYMBOL_LEN];
 | |
|   SYMBOL_INFO* symbol = (SYMBOL_INFO*)&symbolScratch;
 | |
|   IMAGEHLP_MODULE64 moduleInfo;
 | |
|   DWORD64 offset;
 | |
|   DWORD imageType;
 | |
|   int skip = 0;
 | |
|   int depth = 0;
 | |
| 
 | |
|   if (pExceptionInfo == NULL)
 | |
|   { memset(&context, 0, sizeof(CONTEXT));
 | |
|     // If we dont have the context, then we can get the current one from the CPU
 | |
|     // However, we should skip the first N frames, since these relate to the
 | |
|     // exception handler itself
 | |
|     // Obviously N is a magic number - it might differ if this code is modified!
 | |
| #if _WIN32_WINNT > 0x0500
 | |
|     // Good, just use RtlCaptureContext
 | |
|     skip = 2;
 | |
|     RtlCaptureContext(&context);
 | |
| #else
 | |
|     // For earlier than WinXPsp1 we have to do some weird stuff
 | |
|     // For win32, we can use inline assembly to get eip, esp and ebp but
 | |
|     // the MSVC2005 compiler refuses to emit inline assembly for AMD64
 | |
|     // Luckily, the oldest AMD64 build of Windows is XP, so we should be able to
 | |
|     // use RtlCaptureContext!
 | |
| #ifdef WIN64
 | |
| #error You appear to have a 64 bit build of a pre-XP version of Windows?!
 | |
| #else
 | |
|     skip = 2;
 | |
|     __asm
 | |
|     { call steal_eip
 | |
|       steal_eip:
 | |
|       pop eax
 | |
|       mov context.Eip, eax
 | |
|       mov eax, ebp
 | |
|       mov context.Ebp, eax
 | |
|       mov eax, esp
 | |
|       mov context.Esp, eax
 | |
|     }
 | |
| #endif
 | |
| 
 | |
| #endif
 | |
|   } else
 | |
|   { context = *(pExceptionInfo->ContextRecord);
 | |
|   }
 | |
| 
 | |
|   ZeroMemory(&frame, sizeof( STACKFRAME64));
 | |
|   memset(&moduleInfo,0,sizeof(IMAGEHLP_MODULE64));
 | |
|   moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
 | |
|   rc = SymInitialize(hProcess, NULL, TRUE);
 | |
|   if (rc == 0)
 | |
|     return 0;
 | |
| 
 | |
| #ifdef _WIN64
 | |
|    imageType = IMAGE_FILE_MACHINE_AMD64;
 | |
|    frame.AddrPC.Offset = context.Rip;
 | |
|    frame.AddrFrame.Offset = context.Rsp;
 | |
|    frame.AddrStack.Offset = context.Rsp;
 | |
| #else
 | |
|    imageType = IMAGE_FILE_MACHINE_I386;
 | |
|    frame.AddrPC.Offset = context.Eip;
 | |
|    frame.AddrFrame.Offset = context.Ebp;
 | |
|    frame.AddrStack.Offset = context.Esp;
 | |
| #endif
 | |
|    frame.AddrPC.Mode = AddrModeFlat;
 | |
|    frame.AddrFrame.Mode = AddrModeFlat;
 | |
|    frame.AddrStack.Mode = AddrModeFlat;
 | |
| 
 | |
|    while(depth < MAX_DEPTH &&
 | |
| 	 (rc =  StackWalk64(imageType,
 | |
| 			    hProcess,
 | |
| 			    hThread,
 | |
| 			    &frame,
 | |
| 			    &context,
 | |
| 			    NULL,
 | |
| 			    SymFunctionTableAccess64,
 | |
| 			    SymGetModuleBase64,
 | |
| 			    NULL)) != 0)
 | |
|    { int hasModule = 0;
 | |
|      BOOL hasSymbol = FALSE;
 | |
| 
 | |
|      if (skip > 0)
 | |
|      { skip--;
 | |
|        continue;
 | |
|      }
 | |
| 
 | |
|      memset(symbol, 0, sizeof(SYMBOL_INFO) + MAX_SYMBOL_LEN);
 | |
|      symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
 | |
|      symbol->MaxNameLen = MAX_SYMBOL_LEN;
 | |
|      trace->frame[depth].offset = frame.AddrPC.Offset;
 | |
|      hasModule = SymGetModuleInfo64(hProcess, frame.AddrPC.Offset, &moduleInfo);
 | |
| 
 | |
|      if (hasModule == 0)
 | |
|      {
 | |
|        // Note that this CAN be caused by a very out of date dbghelp.dll,
 | |
|        // like the one that ships with Windows XP
 | |
|        // Dropping version 6.x into the bin directory can magically
 | |
|        // make this work. At least we will have the offset
 | |
|        trace->frame[depth].name[0] = '\0';
 | |
|        trace->frame[depth].module[0] = '\0';
 | |
|        trace->frame[depth].module_reason = GetLastError();
 | |
|      } else
 | |
|      { hasSymbol = SymFromAddr(hProcess, frame.AddrPC.Offset, &offset, symbol);
 | |
|        strncpy(trace->frame[depth].module,
 | |
| 	       moduleInfo.ImageName,
 | |
| 	       MAX_MODULE_NAME_LENGTH);
 | |
|        trace->frame[depth].module[MAX_MODULE_NAME_LENGTH-1] = '\0';
 | |
|        trace->frame[depth].module_reason = 0;
 | |
|        if (hasSymbol)
 | |
|        { strncpy(trace->frame[depth].name,
 | |
| 		 symbol->Name,
 | |
| 		 MAX_FUNCTION_NAME_LENGTH);
 | |
|          trace->frame[depth].name[MAX_FUNCTION_NAME_LENGTH-1] = '\0';
 | |
|        } else
 | |
|        { trace->frame[depth].name[0] = '\0';
 | |
|        }
 | |
|      }
 | |
|      depth++;
 | |
|    }
 | |
|    return depth;
 | |
| }
 | |
| 
 | |
| void
 | |
| win_save_backtrace(const char *why, PEXCEPTION_POINTERS pExceptionInfo)
 | |
| { btrace *bt = get_trace_store();
 | |
|   if ( bt )
 | |
|   { int current = next_btrace_id(bt);
 | |
|     btrace_stack *s = &bt->dumps[current];
 | |
|     LOCK();
 | |
|     s->depth = backtrace(s, pExceptionInfo);
 | |
|     UNLOCK();
 | |
|     s->name = why;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void save_backtrace(const char *why)
 | |
| { win_save_backtrace(why, NULL);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| print_trace(btrace *bt, int me)
 | |
| { btrace_stack *s = &bt->dumps[me];
 | |
|   if ( s->name )
 | |
|   { int depth;
 | |
| 
 | |
|     Sdprintf("Stack trace labeled \"%s\":\n", s->name);
 | |
|     for(depth=0; depth<s->depth; depth++)
 | |
|     { Sdprintf("  [%d] <%s>:%s+%p\n", depth,
 | |
|                (s->frame[depth].module[0] == 0) ? "unknown module"
 | |
| 						: s->frame[depth].module,
 | |
| 	       s->frame[depth].name,
 | |
| 	       (void*)s->frame[depth].offset);
 | |
|     }
 | |
|   } else
 | |
|   { Sdprintf("No stack trace\n");
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void
 | |
| print_backtrace(int last)		/* 1..SAVE_TRACES */
 | |
| { btrace *bt = get_trace_store();
 | |
| 
 | |
|   if ( bt )
 | |
|   { int me = bt->current-last;
 | |
|     if ( me < 0 )
 | |
|       me += SAVE_TRACES;
 | |
| 
 | |
|     print_trace(bt, me);
 | |
|   } else
 | |
|   { Sdprintf("No backtrace store?\n");
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| print_backtrace_named(const char *why)
 | |
| { btrace *bt = get_trace_store();
 | |
| 
 | |
|   if ( bt )
 | |
|   { int me = bt->current-1;
 | |
| 
 | |
|     for(;;)
 | |
|     { if ( --me < 0 )
 | |
| 	me += SAVE_TRACES;
 | |
|       if ( bt->dumps[me].name && strcmp(bt->dumps[me].name, why) == 0 )
 | |
|       { print_trace(bt, me);
 | |
| 	return;
 | |
|       }
 | |
|       if ( me == bt->current-1 )
 | |
| 	break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Sdprintf("No backtrace named %s\n", why);
 | |
| }
 | |
| 
 | |
| static LONG WINAPI crashHandler(PEXCEPTION_POINTERS pExceptionInfo)
 | |
| { win_save_backtrace("crash", pExceptionInfo);
 | |
|   print_backtrace_named("crash");
 | |
|   abort();
 | |
| 
 | |
|   return EXCEPTION_CONTINUE_SEARCH; /* ? */
 | |
| }
 | |
| 
 | |
| void
 | |
| initBackTrace(void)
 | |
| { SetUnhandledExceptionFilter(crashHandler);
 | |
| }
 | |
| 
 | |
| #endif /*__WINDOWS__*/
 | |
| 
 | |
| 
 | |
| 	         /*******************************
 | |
| 		 *   FALLBACK IMPLEMENTATION	*
 | |
| 		 *******************************/
 | |
| 
 | |
| 
 | |
| #ifndef BTRACE_DONE
 | |
| 
 | |
| void
 | |
| save_backtrace(const char *why)
 | |
| {
 | |
| }
 | |
| 
 | |
| void
 | |
| btrace_destroy(struct btrace *bt)
 | |
| {
 | |
| }
 | |
| 
 | |
| void
 | |
| print_backtrace(int last)
 | |
| { Sdprintf("%s:%d C-stack dumps are not supported on this platform\n",
 | |
| 	   __FILE__, __LINE__);
 | |
| }
 | |
| 
 | |
| void
 | |
| print_backtrace_named(const char *why)
 | |
| { Sdprintf("%s:%d C-stack dumps are not supported on this platform\n",
 | |
| 	   __FILE__, __LINE__);
 | |
| }
 | |
| 
 | |
| void
 | |
| initBackTrace(void)
 | |
| {
 | |
| }
 | |
| 
 | |
| #endif /*BTRACE_DONE*/
 |