2012-02-01 20:51:24 +00:00
|
|
|
/* $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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-16 12:28:37 +00:00
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-01 20:51:24 +00:00
|
|
|
void
|
|
|
|
save_backtrace(const char *why)
|
|
|
|
{ btrace *bt = get_trace_store();
|
|
|
|
|
|
|
|
if ( bt )
|
2013-01-16 12:28:37 +00:00
|
|
|
{ btrace_stack *s;
|
2012-02-01 20:51:24 +00:00
|
|
|
unw_cursor_t cursor; unw_context_t uc;
|
|
|
|
int depth;
|
2013-01-16 12:28:37 +00:00
|
|
|
int current = next_btrace_id(bt);
|
2012-02-01 20:51:24 +00:00
|
|
|
|
2013-01-16 12:28:37 +00:00
|
|
|
s = &bt->dumps[current];
|
2012-02-01 20:51:24 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-16 12:28:37 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-01 20:51:24 +00:00
|
|
|
void
|
|
|
|
save_backtrace(const char *why)
|
|
|
|
{ btrace *bt = get_trace_store();
|
|
|
|
|
|
|
|
if ( bt )
|
|
|
|
{ void *array[100];
|
|
|
|
size_t frames;
|
2013-01-16 12:28:37 +00:00
|
|
|
int current = next_btrace_id(bt);
|
2012-02-01 20:51:24 +00:00
|
|
|
|
|
|
|
frames = backtrace(array, sizeof(array)/sizeof(void *));
|
2013-01-16 12:28:37 +00:00
|
|
|
bt->sizes[current] = frames;
|
|
|
|
if ( bt->symbols[current] )
|
|
|
|
free(bt->symbols[current]);
|
|
|
|
bt->symbols[current] = backtrace_symbols(array, frames);
|
|
|
|
bt->why[current] = why;
|
2012-02-01 20:51:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2013-01-16 12:28:37 +00:00
|
|
|
#define LOCK() PL_LOCK(L_CSTACK)
|
|
|
|
#define UNLOCK() PL_UNLOCK(L_CSTACK)
|
|
|
|
|
2012-02-01 20:51:24 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-01-16 12:28:37 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2012-02-01 20:51:24 +00:00
|
|
|
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 )
|
2013-01-16 12:28:37 +00:00
|
|
|
{ int current = next_btrace_id(bt);
|
|
|
|
btrace_stack *s = &bt->dumps[current];
|
|
|
|
LOCK();
|
2012-02-01 20:51:24 +00:00
|
|
|
s->depth = backtrace(s, pExceptionInfo);
|
2013-01-16 12:28:37 +00:00
|
|
|
UNLOCK();
|
2012-02-01 20:51:24 +00:00
|
|
|
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*/
|