new SWI file.
This commit is contained in:
parent
4c6164034e
commit
616ae30138
655
os/pl-cstack.c
Normal file
655
os/pl-cstack.c
Normal file
@ -0,0 +1,655 @@
|
||||
/* $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;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
save_backtrace(const char *why)
|
||||
{ btrace *bt = get_trace_store();
|
||||
|
||||
if ( bt )
|
||||
{ btrace_stack *s = &bt->dumps[bt->current];
|
||||
unw_cursor_t cursor; unw_context_t uc;
|
||||
int depth;
|
||||
|
||||
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;
|
||||
|
||||
if ( ++bt->current == SAVE_TRACES )
|
||||
bt->current = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
save_backtrace(const char *why)
|
||||
{ btrace *bt = get_trace_store();
|
||||
|
||||
if ( bt )
|
||||
{ void *array[100];
|
||||
size_t frames;
|
||||
|
||||
frames = backtrace(array, sizeof(array)/sizeof(void *));
|
||||
bt->sizes[bt->current] = frames;
|
||||
if ( bt->symbols[bt->current] )
|
||||
free(bt->symbols[bt->current]);
|
||||
bt->symbols[bt->current] = backtrace_symbols(array, frames);
|
||||
bt->why[bt->current] = why;
|
||||
if ( ++bt->current == SAVE_TRACES )
|
||||
bt->current = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
EXCEPTION_POINTERS *pExp = NULL;
|
||||
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 )
|
||||
{ btrace_stack *s = &bt->dumps[bt->current];
|
||||
s->depth = backtrace(s, pExceptionInfo);
|
||||
s->name = why;
|
||||
if ( ++bt->current == SAVE_TRACES )
|
||||
bt->current = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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*/
|
Reference in New Issue
Block a user