split out backtracing and symbol lookup
add support for backtracing other threads
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
#include "global.h"
|
||||
#include "Backtrace.h"
|
||||
#include "RageUtil.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if defined(BACKTRACE_METHOD_X86_LINUX)
|
||||
#include "LinuxThreadHelpers.h"
|
||||
|
||||
void GetCurrentBacktraceContext( BacktraceContext *ctx )
|
||||
{
|
||||
register void *esp __asm__ ("esp");
|
||||
ctx->esp = (long) esp;
|
||||
ctx->eip = (long) GetThreadBacktraceContext;
|
||||
ctx->ebp = (long) __builtin_frame_address(0);
|
||||
}
|
||||
|
||||
int GetThreadBacktraceContext( int ThreadID, BacktraceContext *ctx )
|
||||
{
|
||||
ctx->pid = ThreadID;
|
||||
|
||||
if( ThreadID != GetCurrentThreadId() )
|
||||
return GetThreadContext( ThreadID, ctx );
|
||||
|
||||
GetCurrentBacktraceContext( ctx );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *itoa(unsigned n)
|
||||
{
|
||||
static char ret[32];
|
||||
char *p = ret;
|
||||
for( int div = 1000000000; div > 0; div /= 10 )
|
||||
{
|
||||
*p++ = (n / div) + '0';
|
||||
n %= div;
|
||||
}
|
||||
*p = 0;
|
||||
p = ret;
|
||||
while( p[0] == '0' && p[1] )
|
||||
++p;
|
||||
return p;
|
||||
}
|
||||
|
||||
static int xtoi( const char *hex )
|
||||
{
|
||||
int ret = 0;
|
||||
while( 1 )
|
||||
{
|
||||
int val = -1;
|
||||
if( *hex >= '0' && *hex <= '9' )
|
||||
val = *hex - '0';
|
||||
else if( *hex >= 'A' && *hex <= 'F' )
|
||||
val = *hex - 'A' + 10;
|
||||
else if( *hex >= 'a' && *hex <= 'f' )
|
||||
val = *hex - 'a' + 10;
|
||||
else
|
||||
break;
|
||||
hex++;
|
||||
|
||||
ret *= 16;
|
||||
ret += val;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum { READABLE_ONLY=1, EXECUTABLE_ONLY=2 };
|
||||
static int get_readable_ranges( const void **starts, const void **ends, int size, int type=READABLE_ONLY )
|
||||
{
|
||||
char path[PATH_MAX] = "/proc/";
|
||||
strcat( path, itoa(getpid()) );
|
||||
strcat( path, "/maps" );
|
||||
|
||||
int fd = open(path, O_RDONLY);
|
||||
if( fd == -1 )
|
||||
return false;
|
||||
|
||||
/* Format:
|
||||
*
|
||||
* 402dd000-402de000 rw-p 00010000 03:03 16815669 /lib/libnsl-2.3.1.so
|
||||
* or
|
||||
* bfffb000-c0000000 rwxp ffffc000 00:00 0
|
||||
*
|
||||
* Look for the range that includes the stack pointer. */
|
||||
char file[1024];
|
||||
int file_used = 0;
|
||||
bool eof = false;
|
||||
int got = 0;
|
||||
while( !eof && got < size-1 )
|
||||
{
|
||||
int ret = read( fd, file+file_used, sizeof(file) - file_used);
|
||||
if( ret < int(sizeof(file)) - file_used)
|
||||
eof = true;
|
||||
|
||||
file_used += ret;
|
||||
|
||||
/* Parse lines. */
|
||||
while( got < size )
|
||||
{
|
||||
char *p = (char *) memchr( file, '\n', file_used );
|
||||
if( p == NULL )
|
||||
break;
|
||||
*p++ = 0;
|
||||
|
||||
char line[1024];
|
||||
strcpy( line, file );
|
||||
memmove(file, p, file_used);
|
||||
file_used -= p-file;
|
||||
|
||||
/* Search for the hyphen. */
|
||||
char *hyphen = strchr( line, '-' );
|
||||
if( hyphen == NULL )
|
||||
continue; /* Parse error. */
|
||||
|
||||
|
||||
/* Search for the space. */
|
||||
char *space = strchr( hyphen, ' ' );
|
||||
if( space == NULL )
|
||||
continue; /* Parse error. */
|
||||
|
||||
/* " rwxp". If space[1] isn't 'r', then the block isn't readable. */
|
||||
if( type & READABLE_ONLY )
|
||||
if( strlen(space) < 2 || space[1] != 'r' )
|
||||
continue;
|
||||
/* " rwxp". If space[3] isn't 'x', then the block isn't readable. */
|
||||
if( type & EXECUTABLE_ONLY )
|
||||
if( strlen(space) < 4 || space[3] != 'x' )
|
||||
continue;
|
||||
|
||||
*starts++ = (const void *) xtoi( line );
|
||||
*ends++ = (const void *) xtoi( hyphen+1 );
|
||||
++got;
|
||||
}
|
||||
|
||||
if( file_used == sizeof(file) )
|
||||
{
|
||||
/* Line longer than the buffer. Weird; bail. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
*starts++ = NULL;
|
||||
*ends++ = NULL;
|
||||
|
||||
return got;
|
||||
}
|
||||
|
||||
/* If the address is readable (eg. reading it won't cause a segfault), return
|
||||
* the block it's in. Otherwise, return -1. */
|
||||
static int find_address( const void *p, const void **starts, const void **ends )
|
||||
{
|
||||
for( int i = 0; starts[i]; ++i )
|
||||
{
|
||||
/* Found it. */
|
||||
if( starts[i] <= p && p < ends[i] )
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void *SavedStackPointer = NULL;
|
||||
|
||||
void InitializeBacktrace()
|
||||
{
|
||||
static bool bInitialized = false;
|
||||
if( bInitialized )
|
||||
return;
|
||||
bInitialized = true;
|
||||
|
||||
/* We might have a different stack in the signal handler. Record a pointer
|
||||
* that lies in the real stack, so we can look it up later. */
|
||||
register void *esp __asm__ ("esp");
|
||||
SavedStackPointer = esp;
|
||||
}
|
||||
|
||||
/* backtrace() for x86 Linux, tested with kernel 2.4.18, glibc 2.3.1. */
|
||||
static void do_backtrace( const void **buf, size_t size, BacktraceContext *ctx )
|
||||
{
|
||||
/* Read /proc/pid/maps to find the address range of the stack. */
|
||||
const void *readable_begin[1024], *readable_end[1024];
|
||||
get_readable_ranges( readable_begin, readable_end, 1024 );
|
||||
|
||||
/* Find the stack memory blocks. */
|
||||
const int stack_block1 = find_address( (void *) ctx->esp, readable_begin, readable_end );
|
||||
const int stack_block2 = find_address( SavedStackPointer, readable_begin, readable_end );
|
||||
|
||||
/* This matches the layout of the stack. The frame pointer makes the
|
||||
* stack a linked list. */
|
||||
struct StackFrame
|
||||
{
|
||||
StackFrame *link;
|
||||
char *return_address;
|
||||
|
||||
/* These are only relevant if the frame is a signal trampoline. */
|
||||
int signal;
|
||||
sigcontext sig;
|
||||
};
|
||||
|
||||
StackFrame *frame = (StackFrame *) ctx->ebp;
|
||||
|
||||
unsigned i=0;
|
||||
if( i < size-1 ) // -1 for NULL
|
||||
buf[i++] = (void *) ctx->eip;
|
||||
|
||||
while( i < size-1 ) // -1 for NULL
|
||||
{
|
||||
/* Make sure that this frame address is readable, and is on the stack. */
|
||||
int val = find_address(frame, readable_begin, readable_end);
|
||||
if( val == -1 )
|
||||
break;
|
||||
if( val != stack_block1 && val != stack_block2 )
|
||||
break;
|
||||
|
||||
/* XXX */
|
||||
//if( frame->return_address == (void*) CrashSignalHandler )
|
||||
// continue;
|
||||
|
||||
/*
|
||||
* The stack return stub is:
|
||||
*
|
||||
* 0x401139d8 <sigaction+280>: pop %eax 0x58
|
||||
* 0x401139d9 <sigaction+281>: mov $0x77,%eax 0xb8 0x77 0x00 0x00 0x00
|
||||
* 0x401139de <sigaction+286>: int $0x80 0xcd 0x80
|
||||
*
|
||||
* This will be different if using realtime signals, as will the stack layout.
|
||||
*
|
||||
* If we detect this, it means this is a stack frame returning from a signal.
|
||||
* Ignore the return_address and use the sigcontext instead.
|
||||
*/
|
||||
const char comp[] = { 0x58, 0xb8, 0x77, 0x0, 0x0, 0x0, 0xcd, 0x80 };
|
||||
bool is_signal_return = true;
|
||||
|
||||
/* Ugh. Linux 2.6 is putting the return address in a place that isn't listed
|
||||
* as readable in /proc/pid/maps. This is probably brittle. */
|
||||
if( frame->return_address != (void*)0xffffe420 &&
|
||||
find_address(frame->return_address, readable_begin, readable_end) == -1)
|
||||
is_signal_return = false;
|
||||
|
||||
for( unsigned pos = 0; is_signal_return && pos < sizeof(comp); ++pos )
|
||||
if(frame->return_address[pos] != comp[pos])
|
||||
is_signal_return = false;
|
||||
|
||||
void *to_add = NULL;
|
||||
if( is_signal_return )
|
||||
{
|
||||
/* Mark the signal trampoline. */
|
||||
if( i < size-1 )
|
||||
buf[i++] = BACKTRACE_SIGNAL_TRAMPOLINE;
|
||||
|
||||
to_add = (void *) frame->sig.eip;
|
||||
}
|
||||
else
|
||||
to_add = frame->return_address;
|
||||
|
||||
if( i < size-1 && to_add )
|
||||
buf[i++] = to_add;
|
||||
|
||||
/* frame always goes down. Make sure it doesn't go up; that could
|
||||
* cause an infinite loop. */
|
||||
if( frame->link <= frame )
|
||||
break;
|
||||
|
||||
frame = frame->link;
|
||||
}
|
||||
|
||||
buf[i] = NULL;
|
||||
}
|
||||
|
||||
void GetBacktrace( const void **buf, size_t size, BacktraceContext *ctx )
|
||||
{
|
||||
InitializeBacktrace();
|
||||
|
||||
BacktraceContext CurrentCtx;
|
||||
if( ctx == NULL )
|
||||
{
|
||||
ctx = &CurrentCtx;
|
||||
GetCurrentBacktraceContext( &CurrentCtx );
|
||||
}
|
||||
|
||||
|
||||
do_backtrace( buf, size, ctx );
|
||||
}
|
||||
#elif defined(BACKTRACE_METHOD_BACKTRACE)
|
||||
#include <execinfo.h>
|
||||
|
||||
void InitializeBacktrace() { }
|
||||
|
||||
void GetBacktrace( const void **buf, size_t size, BacktraceContext *ctx )
|
||||
{
|
||||
InitializeBacktrace();
|
||||
|
||||
int retsize = backtrace( buf, size-1 );
|
||||
|
||||
/* Remove any NULL entries. We want to null-terminate the list, and null entries are useless. */
|
||||
for( int i = retsize-1; i >= 0; --i )
|
||||
{
|
||||
if( buf[i] != NULL )
|
||||
continue;
|
||||
|
||||
memmove( &buf[i], &buf[i]+1, retsize-i-1 );
|
||||
}
|
||||
|
||||
buf[retsize] = NULL;
|
||||
}
|
||||
#elif defined(BACKTRACE_METHOD_POWERPC_DARWIN)
|
||||
|
||||
#include "archutils/Darwin/Crash.h"
|
||||
typedef struct Frame {
|
||||
Frame *stackPointer;
|
||||
long conditionReg;
|
||||
void *linkReg;
|
||||
} *FramePtr;
|
||||
|
||||
void InitializeBacktrace() { }
|
||||
|
||||
void GetBacktrace( const void **buf, size_t size, BacktraceContext *ctx )
|
||||
{
|
||||
FramePtr frame = FramePtr(GetCrashedFramePtr());
|
||||
unsigned i;
|
||||
for (i=0; frame && frame->linkReg && i<size; ++i, frame=frame->stackPointer)
|
||||
buf[i] = frame->linkReg;
|
||||
i = (i == size ? size - 1 : i);
|
||||
buf[i] = NULL;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#warning Undefined BACKTRACE_METHOD_*
|
||||
void InitializeBacktrace() { }
|
||||
|
||||
void GetBacktrace( const void **buf, size_t size, BacktraceContext *ctx )
|
||||
{
|
||||
buf[0] = BACKTRACE_METHOD_NOT_AVAILABLE;
|
||||
buf[1] = NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef BACKTRACE_H
|
||||
#define BACKTRACE_H
|
||||
|
||||
/* This API works like backtrace_pointers(), to retrieve a stack trace. */
|
||||
|
||||
/* This contains the information necessary to backtrace a thread. */
|
||||
struct BacktraceContext
|
||||
{
|
||||
#if defined(LINUX)
|
||||
long eip, esp, ebp;
|
||||
pid_t pid;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Initialize. This is optional. If not called explicitly, it will be
|
||||
* called as necessary. This may do things that are not safe to do in
|
||||
* crash conditions. */
|
||||
void InitializeBacktrace();
|
||||
|
||||
/* Retrieve up to size-1 backtrace pointers in buf. The array will be
|
||||
* null-terminated. If ctx is NULL, retrieve the current backtrace; otherwise
|
||||
* retrieve a backtrace for the given context. (Not all backtracers may
|
||||
* support contexts.) */
|
||||
void GetBacktrace( const void **buf, size_t size, BacktraceContext *ctx = NULL );
|
||||
|
||||
/* Set up a BacktraceContext to get a backtrace for a thread. ThreadID may
|
||||
* be the current thread. */
|
||||
int GetThreadBacktraceContext( int ThreadID, BacktraceContext *ctx );
|
||||
void GetCurrentBacktraceContext( BacktraceContext *ctx );
|
||||
|
||||
#define BACKTRACE_METHOD_NOT_AVAILABLE ((void*) -1)
|
||||
#define BACKTRACE_SIGNAL_TRAMPOLINE ((void*) -2)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
/* for dladdr: */
|
||||
#define __USE_GNU
|
||||
#include "global.h"
|
||||
#include "BacktraceNames.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "RageUtil.h"
|
||||
|
||||
#if defined(DARWIN)
|
||||
#include "archutils/Darwin/Crash.h"
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_LIBIBERTY)
|
||||
#include "libiberty.h"
|
||||
|
||||
/* This is in libiberty. Where is it declared? */
|
||||
extern "C" {
|
||||
char *cplus_demangle (const char *mangled, int options);
|
||||
}
|
||||
|
||||
void BacktraceNames::Demangle()
|
||||
{
|
||||
char *f = cplus_demangle(Symbol, 0);
|
||||
if(!f)
|
||||
return;
|
||||
Symbol = f;
|
||||
free(f);
|
||||
}
|
||||
#elif defined(HAVE_CXA_DEMANGLE)
|
||||
#include <cxxabi.h>
|
||||
|
||||
void BacktraceNames::Demangle()
|
||||
{
|
||||
/* demangle the name using __cxa_demangle() if needed */
|
||||
if( Symbol.substr(0, 2) != "_Z" )
|
||||
return;
|
||||
|
||||
int status = 0;
|
||||
char *name = abi::__cxa_demangle( Symbol, 0, 0, &status );
|
||||
if( name )
|
||||
{
|
||||
Symbol = name;
|
||||
free( name );
|
||||
return;
|
||||
}
|
||||
|
||||
switch( status )
|
||||
{
|
||||
case -1:
|
||||
fprintf( stderr, "Out of memory\n" );
|
||||
break;
|
||||
case -2:
|
||||
fprintf( stderr, "Invalid mangled name: %s.\n", Symbol.c_str() );
|
||||
break;
|
||||
case -3:
|
||||
fprintf( stderr, "Invalid arguments.\n" );
|
||||
break;
|
||||
default:
|
||||
fprintf( stderr, "Unexpected __cxa_demangle status: %i", status );
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
void BacktraceNames::Demangle() { }
|
||||
#endif
|
||||
|
||||
|
||||
CString BacktraceNames::Format() const
|
||||
{
|
||||
CString ShortenedPath = File;
|
||||
if( ShortenedPath != "" )
|
||||
{
|
||||
/* Abbreviate the module name. */
|
||||
unsigned slash = ShortenedPath.rfind('/');
|
||||
if( slash != ShortenedPath.npos )
|
||||
ShortenedPath = ShortenedPath.substr(slash+1);
|
||||
ShortenedPath = CString("(") + ShortenedPath + ")";
|
||||
}
|
||||
|
||||
CString ret = ssprintf( "%08x: ", Address );
|
||||
if( Symbol != "" )
|
||||
ret += Symbol + " ";
|
||||
ret += ShortenedPath;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#if defined(BACKTRACE_LOOKUP_METHOD_DLADDR)
|
||||
/* This version simply asks libdl, which is more robust. */
|
||||
#include <dlfcn.h>
|
||||
void BacktraceNames::FromAddr( const void *p )
|
||||
{
|
||||
Address = (int) p;
|
||||
|
||||
Dl_info di;
|
||||
if( !dladdr(p, &di) )
|
||||
return;
|
||||
|
||||
Symbol = di.dli_sname;
|
||||
File = di.dli_fname;
|
||||
Offset = (char*)(p)-(char*)di.dli_saddr;
|
||||
}
|
||||
|
||||
#elif defined(BACKTRACE_LOOKUP_METHOD_BACKTRACE_SYMBOLS)
|
||||
/* This version parses backtrace_symbols(), an doesn't need libdl. */
|
||||
#include <execinfo.h>
|
||||
void BacktraceNames::FromAddr( const void *p )
|
||||
{
|
||||
Address = (int) p;
|
||||
|
||||
char **foo = backtrace_symbols(&p, 1);
|
||||
if( foo == NULL )
|
||||
return;
|
||||
FromString( foo[0] );
|
||||
free(foo);
|
||||
}
|
||||
|
||||
/* "path(mangled name+offset) [address]" */
|
||||
void BacktraceNames::FromString( CString s )
|
||||
{
|
||||
/* Hacky parser. I don't want to use regexes in the crash handler. */
|
||||
CString MangledAndOffset, sAddress;
|
||||
unsigned pos = 0;
|
||||
while( pos < s.size() && s[pos] != '(' && s[pos] != '[' )
|
||||
File += s[pos++];
|
||||
TrimRight( File );
|
||||
TrimLeft( File );
|
||||
|
||||
if( pos < s.size() && s[pos] == '(' )
|
||||
{
|
||||
pos++;
|
||||
while( pos < s.size() && s[pos] != ')' )
|
||||
MangledAndOffset += s[pos++];
|
||||
}
|
||||
|
||||
if( MangledAndOffset != "" )
|
||||
{
|
||||
unsigned plus = MangledAndOffset.rfind('+');
|
||||
|
||||
if(plus == MangledAndOffset.npos)
|
||||
{
|
||||
Symbol = MangledAndOffset;
|
||||
Offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Symbol = MangledAndOffset.substr(0, plus);
|
||||
CString str = MangledAndOffset.substr(plus);
|
||||
if( sscanf(str, "%i", &Offset) != 1 )
|
||||
Offset=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(BACKTRACE_LOOKUP_METHOD_ATOS)
|
||||
void BacktraceNames::FromAddr( const void *p )
|
||||
{
|
||||
int fds[2];
|
||||
int out = fileno(stdout);
|
||||
pid_t pid;
|
||||
pid_t ppid = getppid(); /* Do this before fork()ing! */
|
||||
|
||||
Offset = 0;
|
||||
Address = long(p);
|
||||
|
||||
if (pipe(fds) != 0)
|
||||
{
|
||||
fprintf(stderr, "FromAddr pipe() failed: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
fprintf(stderr, "FromAddr fork() failed: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
close(fds[0]);
|
||||
for (int fd = 3; fd < 1024; ++fd)
|
||||
if (fd != fds[1])
|
||||
close(fd);
|
||||
dup2(fds[1], out);
|
||||
close(fds[1]);
|
||||
|
||||
char *addy;
|
||||
asprintf(&addy, "0x%x", long(p));
|
||||
char *p;
|
||||
asprintf(&p, "%d", ppid);
|
||||
|
||||
execl("/usr/bin/atos", "/usr/bin/atos", "-p", p, addy, NULL);
|
||||
|
||||
fprintf(stderr, "execl(atos) failed: %s\n", strerror(errno));
|
||||
free(addy);
|
||||
free(p);
|
||||
close(out);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
close(fds[1]);
|
||||
char f[1024];
|
||||
bzero(f, 1024);
|
||||
int len = read(fds[0], f, 1024);
|
||||
|
||||
Symbol = "";
|
||||
File = "";
|
||||
|
||||
if (len == -1)
|
||||
{
|
||||
fprintf(stderr, "FromAddr read() failed: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
CStringArray mangledAndFile;
|
||||
|
||||
split(f, " ", mangledAndFile, true);
|
||||
if (mangledAndFile.size() == 0)
|
||||
return;
|
||||
Symbol = mangledAndFile[0];
|
||||
/* eg
|
||||
* -[NSApplication run]
|
||||
* +[SomeClass initialize]
|
||||
*/
|
||||
if (Symbol[0] == '-' || Symbol[0] == '+')
|
||||
{
|
||||
Symbol = mangledAndFile[0] + " " + mangledAndFile[1];
|
||||
/* eg
|
||||
* (crt.c:300)
|
||||
* (AppKit)
|
||||
*/
|
||||
if (mangledAndFile.size() == 3)
|
||||
{
|
||||
File = mangledAndFile[2];
|
||||
unsigned pos = File.find('(');
|
||||
unsigned start = (pos == File.npos ? 0 : pos+1);
|
||||
pos = File.rfind(')') - 1;
|
||||
File = File.substr(start, pos);
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* eg
|
||||
* __start -> _start
|
||||
* _SDL_main -> SDL_main
|
||||
*/
|
||||
if (Symbol[0] == '_')
|
||||
Symbol = Symbol.substr(1);
|
||||
|
||||
/* eg, the full line:
|
||||
* __Z1Ci (in a.out) (asmtest.cc:33)
|
||||
* _main (in a.out) (asmtest.cc:52)
|
||||
*/
|
||||
if (mangledAndFile.size() > 3)
|
||||
{
|
||||
File = mangledAndFile[3];
|
||||
unsigned pos = File.find('(');
|
||||
unsigned start = (pos == File.npos ? 0 : pos+1);
|
||||
pos = File.rfind(')') - 1;
|
||||
File = File.substr(start, pos);
|
||||
}
|
||||
/* eg, the full line:
|
||||
* _main (SDLMain.m:308)
|
||||
* __Z8GameLoopv (crt.c:300)
|
||||
*/
|
||||
else if (mangledAndFile.size() == 3)
|
||||
File = mangledAndFile[2].substr(0, mangledAndFile[2].rfind(')'));
|
||||
}
|
||||
#else
|
||||
#warning Undefined BACKTRACE_LOOKUP_METHOD_*
|
||||
void BacktraceNames::FromAddr( const void *p )
|
||||
{
|
||||
Address = long(p);
|
||||
Offset = 0;
|
||||
Symbol = "";
|
||||
File = "";
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef BACKTRACE_NAMES_H
|
||||
#define BACKTRACE_NAMES_H
|
||||
|
||||
struct BacktraceNames
|
||||
{
|
||||
CString Symbol, File;
|
||||
int Address;
|
||||
int Offset;
|
||||
void FromAddr( const void *p );
|
||||
void FromString( CString str );
|
||||
void Demangle();
|
||||
CString Format() const;
|
||||
BacktraceNames(): Address(0), Offset(0) { }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "RageLog.h" /* for RageLog::GetAdditionalLog, etc, only */
|
||||
#include "RageThreads.h"
|
||||
#include "Backtrace.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
@@ -167,283 +168,6 @@ static const char *itoa(unsigned n)
|
||||
return p;
|
||||
}
|
||||
|
||||
#if defined(BACKTRACE_METHOD_X86_LINUX)
|
||||
static int xtoi( const char *hex )
|
||||
{
|
||||
int ret = 0;
|
||||
while( 1 )
|
||||
{
|
||||
int val = -1;
|
||||
if( *hex >= '0' && *hex <= '9' )
|
||||
val = *hex - '0';
|
||||
else if( *hex >= 'A' && *hex <= 'F' )
|
||||
val = *hex - 'A' + 10;
|
||||
else if( *hex >= 'a' && *hex <= 'f' )
|
||||
val = *hex - 'a' + 10;
|
||||
else
|
||||
break;
|
||||
hex++;
|
||||
|
||||
ret *= 16;
|
||||
ret += val;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum { READABLE_ONLY=1, EXECUTABLE_ONLY=2 };
|
||||
static int get_readable_ranges( const void **starts, const void **ends, int size, int type=READABLE_ONLY )
|
||||
{
|
||||
char path[PATH_MAX] = "/proc/";
|
||||
strcat( path, itoa(getpid()) );
|
||||
strcat( path, "/maps" );
|
||||
|
||||
int fd = open(path, O_RDONLY);
|
||||
if( fd == -1 )
|
||||
return false;
|
||||
|
||||
/* Format:
|
||||
*
|
||||
* 402dd000-402de000 rw-p 00010000 03:03 16815669 /lib/libnsl-2.3.1.so
|
||||
* or
|
||||
* bfffb000-c0000000 rwxp ffffc000 00:00 0
|
||||
*
|
||||
* Look for the range that includes the stack pointer. */
|
||||
char file[1024];
|
||||
int file_used = 0;
|
||||
bool eof = false;
|
||||
int got = 0;
|
||||
while( !eof && got < size-1 )
|
||||
{
|
||||
int ret = read( fd, file+file_used, sizeof(file) - file_used);
|
||||
if( ret < int(sizeof(file)) - file_used)
|
||||
eof = true;
|
||||
|
||||
file_used += ret;
|
||||
|
||||
/* Parse lines. */
|
||||
while( got < size )
|
||||
{
|
||||
char *p = (char *) memchr( file, '\n', file_used );
|
||||
if( p == NULL )
|
||||
break;
|
||||
*p++ = 0;
|
||||
|
||||
char line[1024];
|
||||
strcpy( line, file );
|
||||
memmove(file, p, file_used);
|
||||
file_used -= p-file;
|
||||
|
||||
/* Search for the hyphen. */
|
||||
char *hyphen = strchr( line, '-' );
|
||||
if( hyphen == NULL )
|
||||
continue; /* Parse error. */
|
||||
|
||||
|
||||
/* Search for the space. */
|
||||
char *space = strchr( hyphen, ' ' );
|
||||
if( space == NULL )
|
||||
continue; /* Parse error. */
|
||||
|
||||
/* " rwxp". If space[1] isn't 'r', then the block isn't readable. */
|
||||
if( type & READABLE_ONLY )
|
||||
if( strlen(space) < 2 || space[1] != 'r' )
|
||||
continue;
|
||||
/* " rwxp". If space[3] isn't 'x', then the block isn't readable. */
|
||||
if( type & EXECUTABLE_ONLY )
|
||||
if( strlen(space) < 4 || space[3] != 'x' )
|
||||
continue;
|
||||
|
||||
*starts++ = (const void *) xtoi( line );
|
||||
*ends++ = (const void *) xtoi( hyphen+1 );
|
||||
++got;
|
||||
}
|
||||
|
||||
if( file_used == sizeof(file) )
|
||||
{
|
||||
/* Line longer than the buffer. Weird; bail. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
*starts++ = NULL;
|
||||
*ends++ = NULL;
|
||||
|
||||
return got;
|
||||
}
|
||||
|
||||
/* If the address is readable (eg. reading it won't cause a segfault), return
|
||||
* the block it's in. Otherwise, return -1. */
|
||||
static int find_address( const void *p, const void **starts, const void **ends )
|
||||
{
|
||||
for( int i = 0; starts[i]; ++i )
|
||||
{
|
||||
/* Found it. */
|
||||
if( starts[i] <= p && p < ends[i] )
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void *SavedStackPointer = NULL;
|
||||
|
||||
static void initialize_do_backtrace()
|
||||
{
|
||||
/* We might have a different stack in the signal handler. Record a pointer
|
||||
* that lies in the real stack, so we can look it up later. */
|
||||
register void *esp __asm__ ("esp");
|
||||
SavedStackPointer = esp;
|
||||
}
|
||||
|
||||
/* backtrace() for x86 Linux, tested with kernel 2.4.18, glibc 2.3.1. */
|
||||
static void do_backtrace( const void **buf, size_t size, bool ignore_before_sig = true )
|
||||
{
|
||||
/* Read /proc/pid/maps to find the address range of the stack. */
|
||||
const void *readable_begin[1024], *readable_end[1024];
|
||||
get_readable_ranges( readable_begin, readable_end, 1024 );
|
||||
|
||||
/* Find the stack memory blocks. */
|
||||
register void *esp __asm__ ("esp");
|
||||
const int stack_block1 = find_address( esp, readable_begin, readable_end );
|
||||
const int stack_block2 = find_address( SavedStackPointer, readable_begin, readable_end );
|
||||
|
||||
/* This matches the layout of the stack. The frame pointer makes the
|
||||
* stack a linked list. */
|
||||
struct StackFrame
|
||||
{
|
||||
StackFrame *link;
|
||||
char *return_address;
|
||||
|
||||
/* These are only relevant if the frame is a signal trampoline. */
|
||||
int signal;
|
||||
sigcontext sig;
|
||||
};
|
||||
|
||||
StackFrame *frame = (StackFrame *) __builtin_frame_address(0);
|
||||
|
||||
unsigned i=0;
|
||||
/* If ignore_before_sig is true, don't return stack frames before we find a signal trampoline. */
|
||||
bool got_signal_return = !ignore_before_sig;
|
||||
while( i < size-1 ) // -1 for NULL
|
||||
{
|
||||
/* Make sure that this frame address is readable, and is on the stack. */
|
||||
int val = find_address(frame, readable_begin, readable_end);
|
||||
if( val == -1 )
|
||||
break;
|
||||
if( val != stack_block1 && val != stack_block2 )
|
||||
break;
|
||||
|
||||
if( frame->return_address == (void*) CrashSignalHandler )
|
||||
continue;
|
||||
|
||||
/*
|
||||
* The stack return stub is:
|
||||
*
|
||||
* 0x401139d8 <sigaction+280>: pop %eax 0x58
|
||||
* 0x401139d9 <sigaction+281>: mov $0x77,%eax 0xb8 0x77 0x00 0x00 0x00
|
||||
* 0x401139de <sigaction+286>: int $0x80 0xcd 0x80
|
||||
*
|
||||
* This will be different if using realtime signals, as will the stack layout.
|
||||
*
|
||||
* If we detect this, it means this is a stack frame returning from a signal.
|
||||
* Ignore the return_address and use the sigcontext instead.
|
||||
*/
|
||||
const char comp[] = { 0x58, 0xb8, 0x77, 0x0, 0x0, 0x0, 0xcd, 0x80 };
|
||||
bool is_signal_return = true;
|
||||
|
||||
/* Ugh. Linux 2.6 is putting the return address in a place that isn't listed
|
||||
* as readable in /proc/pid/maps. This is probably brittle. */
|
||||
if( frame->return_address != (void*)0xffffe420 &&
|
||||
find_address(frame->return_address, readable_begin, readable_end) == -1)
|
||||
is_signal_return = false;
|
||||
|
||||
for( unsigned pos = 0; is_signal_return && pos < sizeof(comp); ++pos )
|
||||
if(frame->return_address[pos] != comp[pos])
|
||||
is_signal_return = false;
|
||||
|
||||
void *to_add = NULL;
|
||||
if( is_signal_return )
|
||||
{
|
||||
got_signal_return = true;
|
||||
to_add = (void *) frame->sig.eip;
|
||||
}
|
||||
/* Ignore stack frames before the signal. */
|
||||
else if( got_signal_return )
|
||||
to_add = frame->return_address;
|
||||
|
||||
if( to_add )
|
||||
buf[i++] = to_add;
|
||||
|
||||
/* frame always goes down. Make sure it doesn't go up; that could
|
||||
* cause an infinite loop. */
|
||||
if( frame->link <= frame )
|
||||
break;
|
||||
|
||||
frame = frame->link;
|
||||
}
|
||||
|
||||
buf[i] = NULL;
|
||||
|
||||
/* If we didn't get any frames, our trampoline handling probably failed. Turn
|
||||
* ignore_before_sig off. We'll probably lose the top frame, which is bad.
|
||||
* You can tell that this happened if this function is visible on the returned
|
||||
* call stack. */
|
||||
if( i == 0 && ignore_before_sig )
|
||||
do_backtrace( buf, size, false );
|
||||
}
|
||||
#elif defined(BACKTRACE_METHOD_BACKTRACE)
|
||||
#include <execinfo.h>
|
||||
|
||||
static void initialize_do_backtrace() { }
|
||||
|
||||
static void do_backtrace( const void **buf, size_t size )
|
||||
{
|
||||
int retsize = backtrace( buf, size-1 );
|
||||
|
||||
/* Remove any NULL entries. We want to null-terminate the list, and null entries are useless. */
|
||||
for( int i = retsize-1; i >= 0; --i )
|
||||
{
|
||||
if( buf[i] != NULL )
|
||||
continue;
|
||||
|
||||
memmove( &buf[i], &buf[i]+1, retsize-i-1 );
|
||||
}
|
||||
|
||||
buf[retsize] = NULL;
|
||||
}
|
||||
#elif defined(BACKTRACE_METHOD_POWERPC_DARWIN)
|
||||
#include "archutils/Darwin/Crash.h"
|
||||
typedef struct Frame {
|
||||
Frame *stackPointer;
|
||||
long conditionReg;
|
||||
void *linkReg;
|
||||
} *FramePtr;
|
||||
|
||||
static void initialize_do_backtrace() { }
|
||||
|
||||
static void do_backtrace( const void **buf, size_t size )
|
||||
{
|
||||
FramePtr frame = FramePtr(GetCrashedFramePtr());
|
||||
unsigned i;
|
||||
for (i=0; frame && frame->linkReg && i<size; ++i, frame=frame->stackPointer)
|
||||
buf[i] = frame->linkReg;
|
||||
i = (i == size ? size - 1 : i);
|
||||
buf[i] = NULL;
|
||||
}
|
||||
#else
|
||||
#warning Undefined BACKTRACE_METHOD_*
|
||||
static void initialize_do_backtrace() { }
|
||||
|
||||
static void do_backtrace( const void **buf, size_t size )
|
||||
{
|
||||
buf[0] = BACKTRACE_METHOD_NOT_AVAILABLE;
|
||||
buf[1] = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void CrashSignalHandler( int signal )
|
||||
{
|
||||
if( g_pCrashHandlerArgv0 == NULL )
|
||||
@@ -478,8 +202,25 @@ void CrashSignalHandler( int signal )
|
||||
|
||||
/* Do this early, so functions called below don't end up on the backtrace. */
|
||||
const void *BacktracePointers[BACKTRACE_MAX_SIZE];
|
||||
do_backtrace(BacktracePointers, BACKTRACE_MAX_SIZE);
|
||||
GetBacktrace( BacktracePointers, BACKTRACE_MAX_SIZE );
|
||||
|
||||
/* If we have BACKTRACE_SIGNAL_TRAMPOLINE, remove it and everything before it. */
|
||||
for( int i = 0; BacktracePointers[i]; ++i )
|
||||
{
|
||||
if( BacktracePointers[i] != BACKTRACE_SIGNAL_TRAMPOLINE )
|
||||
continue;
|
||||
|
||||
++i;
|
||||
|
||||
/* Find the terminator. */
|
||||
int end = i;
|
||||
while( BacktracePointers[end] )
|
||||
++end;
|
||||
|
||||
memmove( &BacktracePointers[0], &BacktracePointers[i], sizeof(void*) * end-i-1 );
|
||||
break;
|
||||
}
|
||||
|
||||
/* We need to be very careful, since we're under crash conditions. Let's fork
|
||||
* a process and exec ourself to get a clean environment to work in. */
|
||||
int fds[2];
|
||||
@@ -514,6 +255,6 @@ void CrashSignalHandler( int signal )
|
||||
|
||||
void InitializeCrashHandler()
|
||||
{
|
||||
initialize_do_backtrace();
|
||||
InitializeBacktrace();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
/* for dladdr: */
|
||||
#define __USE_GNU
|
||||
#include "global.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "Backtrace.h"
|
||||
#include "BacktraceNames.h"
|
||||
|
||||
#include "RageUtil.h"
|
||||
#include "CrashHandler.h"
|
||||
#include "CrashHandlerInternal.h"
|
||||
@@ -28,289 +26,6 @@ extern const unsigned version_num;
|
||||
|
||||
const char *g_pCrashHandlerArgv0 = NULL;
|
||||
|
||||
struct BacktraceNames
|
||||
{
|
||||
CString Symbol, File;
|
||||
int Address;
|
||||
int Offset;
|
||||
void FromAddr( void *p );
|
||||
void FromString( CString str );
|
||||
void Demangle();
|
||||
CString Format() const;
|
||||
BacktraceNames(): Address(0), Offset(0) { }
|
||||
};
|
||||
|
||||
#if defined(HAVE_LIBIBERTY)
|
||||
#include "libiberty.h"
|
||||
|
||||
/* This is in libiberty. Where is it declared? */
|
||||
extern "C" {
|
||||
char *cplus_demangle (const char *mangled, int options);
|
||||
}
|
||||
|
||||
void BacktraceNames::Demangle()
|
||||
{
|
||||
char *f = cplus_demangle(Symbol, 0);
|
||||
if(!f)
|
||||
return;
|
||||
Symbol = f;
|
||||
free(f);
|
||||
}
|
||||
#elif defined(HAVE_CXA_DEMANGLE)
|
||||
#include <cxxabi.h>
|
||||
|
||||
void BacktraceNames::Demangle()
|
||||
{
|
||||
/* demangle the name using __cxa_demangle() if needed */
|
||||
if( Symbol.substr(0, 2) != "_Z" )
|
||||
return;
|
||||
|
||||
int status = 0;
|
||||
char *name = abi::__cxa_demangle( Symbol, 0, 0, &status );
|
||||
if( name )
|
||||
{
|
||||
Symbol = name;
|
||||
free( name );
|
||||
return;
|
||||
}
|
||||
|
||||
switch( status )
|
||||
{
|
||||
case -1:
|
||||
fprintf( stderr, "Out of memory\n" );
|
||||
break;
|
||||
case -2:
|
||||
fprintf( stderr, "Invalid mangled name: %s.\n", Symbol.c_str() );
|
||||
break;
|
||||
case -3:
|
||||
fprintf( stderr, "Invalid arguments.\n" );
|
||||
break;
|
||||
default:
|
||||
fprintf( stderr, "Unexpected __cxa_demangle status: %i", status );
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
void BacktraceNames::Demangle() { }
|
||||
#endif
|
||||
|
||||
|
||||
CString BacktraceNames::Format() const
|
||||
{
|
||||
CString ShortenedPath = File;
|
||||
if( ShortenedPath != "" )
|
||||
{
|
||||
/* We have some sort of symbol name, so abbreviate or elide the module name. */
|
||||
if( ShortenedPath == g_pCrashHandlerArgv0 )
|
||||
ShortenedPath = "";
|
||||
else
|
||||
{
|
||||
unsigned slash = ShortenedPath.rfind('/');
|
||||
if( slash != ShortenedPath.npos )
|
||||
ShortenedPath = ShortenedPath.substr(slash+1);
|
||||
ShortenedPath = CString("(") + ShortenedPath + ")";
|
||||
}
|
||||
}
|
||||
|
||||
CString ret = ssprintf( "%08x: ", Address );
|
||||
if( Symbol != "" )
|
||||
ret += Symbol + " ";
|
||||
ret += ShortenedPath;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#if defined(BACKTRACE_LOOKUP_METHOD_DLADDR)
|
||||
/* This version simply asks libdl, which is more robust. */
|
||||
#include <dlfcn.h>
|
||||
void BacktraceNames::FromAddr( void *p )
|
||||
{
|
||||
Address = (int) p;
|
||||
|
||||
Dl_info di;
|
||||
if( !dladdr(p, &di) )
|
||||
return;
|
||||
|
||||
Symbol = di.dli_sname;
|
||||
File = di.dli_fname;
|
||||
Offset = (char*)(p)-(char*)di.dli_saddr;
|
||||
}
|
||||
|
||||
#elif defined(BACKTRACE_LOOKUP_METHOD_BACKTRACE_SYMBOLS)
|
||||
/* This version parses backtrace_symbols(), an doesn't need libdl. */
|
||||
#include <execinfo.h>
|
||||
void BacktraceNames::FromAddr( void *p )
|
||||
{
|
||||
Address = (int) p;
|
||||
|
||||
char **foo = backtrace_symbols(&p, 1);
|
||||
if( foo == NULL )
|
||||
return;
|
||||
FromString( foo[0] );
|
||||
free(foo);
|
||||
}
|
||||
|
||||
/* "path(mangled name+offset) [address]" */
|
||||
void BacktraceNames::FromString( CString s )
|
||||
{
|
||||
/* Hacky parser. I don't want to use regexes in the crash handler. */
|
||||
CString MangledAndOffset, sAddress;
|
||||
unsigned pos = 0;
|
||||
while( pos < s.size() && s[pos] != '(' && s[pos] != '[' )
|
||||
File += s[pos++];
|
||||
TrimRight( File );
|
||||
TrimLeft( File );
|
||||
|
||||
if( pos < s.size() && s[pos] == '(' )
|
||||
{
|
||||
pos++;
|
||||
while( pos < s.size() && s[pos] != ')' )
|
||||
MangledAndOffset += s[pos++];
|
||||
}
|
||||
|
||||
if( MangledAndOffset != "" )
|
||||
{
|
||||
unsigned plus = MangledAndOffset.rfind('+');
|
||||
|
||||
if(plus == MangledAndOffset.npos)
|
||||
{
|
||||
Symbol = MangledAndOffset;
|
||||
Offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Symbol = MangledAndOffset.substr(0, plus);
|
||||
CString str = MangledAndOffset.substr(plus);
|
||||
if( sscanf(str, "%i", &Offset) != 1 )
|
||||
Offset=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(BACKTRACE_LOOKUP_METHOD_ATOS)
|
||||
void BacktraceNames::FromAddr( void *p )
|
||||
{
|
||||
int fds[2];
|
||||
int out = fileno(stdout);
|
||||
pid_t pid;
|
||||
pid_t ppid = getppid(); /* Do this before fork()ing! */
|
||||
|
||||
Offset = 0;
|
||||
Address = long(p);
|
||||
|
||||
if (pipe(fds) != 0)
|
||||
{
|
||||
fprintf(stderr, "FromAddr pipe() failed: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
fprintf(stderr, "FromAddr fork() failed: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
close(fds[0]);
|
||||
for (int fd = 3; fd < 1024; ++fd)
|
||||
if (fd != fds[1])
|
||||
close(fd);
|
||||
dup2(fds[1], out);
|
||||
close(fds[1]);
|
||||
|
||||
char *addy;
|
||||
asprintf(&addy, "0x%x", long(p));
|
||||
char *p;
|
||||
asprintf(&p, "%d", ppid);
|
||||
|
||||
execl("/usr/bin/atos", "/usr/bin/atos", "-p", p, addy, NULL);
|
||||
|
||||
fprintf(stderr, "execl(atos) failed: %s\n", strerror(errno));
|
||||
free(addy);
|
||||
free(p);
|
||||
close(out);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
close(fds[1]);
|
||||
char f[1024];
|
||||
bzero(f, 1024);
|
||||
int len = read(fds[0], f, 1024);
|
||||
|
||||
Symbol = "";
|
||||
File = "";
|
||||
|
||||
if (len == -1)
|
||||
{
|
||||
fprintf(stderr, "FromAddr read() failed: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
CStringArray mangledAndFile;
|
||||
|
||||
split(f, " ", mangledAndFile, true);
|
||||
if (mangledAndFile.size() == 0)
|
||||
return;
|
||||
Symbol = mangledAndFile[0];
|
||||
/* eg
|
||||
* -[NSApplication run]
|
||||
* +[SomeClass initialize]
|
||||
*/
|
||||
if (Symbol[0] == '-' || Symbol[0] == '+')
|
||||
{
|
||||
Symbol = mangledAndFile[0] + " " + mangledAndFile[1];
|
||||
/* eg
|
||||
* (crt.c:300)
|
||||
* (AppKit)
|
||||
*/
|
||||
if (mangledAndFile.size() == 3)
|
||||
{
|
||||
File = mangledAndFile[2];
|
||||
unsigned pos = File.find('(');
|
||||
unsigned start = (pos == File.npos ? 0 : pos+1);
|
||||
pos = File.rfind(')') - 1;
|
||||
File = File.substr(start, pos);
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* eg
|
||||
* __start -> _start
|
||||
* _SDL_main -> SDL_main
|
||||
*/
|
||||
if (Symbol[0] == '_')
|
||||
Symbol = Symbol.substr(1);
|
||||
|
||||
/* eg, the full line:
|
||||
* __Z1Ci (in a.out) (asmtest.cc:33)
|
||||
* _main (in a.out) (asmtest.cc:52)
|
||||
*/
|
||||
if (mangledAndFile.size() > 3)
|
||||
{
|
||||
File = mangledAndFile[3];
|
||||
unsigned pos = File.find('(');
|
||||
unsigned start = (pos == File.npos ? 0 : pos+1);
|
||||
pos = File.rfind(')') - 1;
|
||||
File = File.substr(start, pos);
|
||||
}
|
||||
/* eg, the full line:
|
||||
* _main (SDLMain.m:308)
|
||||
* __Z8GameLoopv (crt.c:300)
|
||||
*/
|
||||
else if (mangledAndFile.size() == 3)
|
||||
File = mangledAndFile[2].substr(0, mangledAndFile[2].rfind(')'));
|
||||
}
|
||||
#else
|
||||
#warning Undefined BACKTRACE_LOOKUP_METHOD_*
|
||||
void BacktraceNames::FromAddr( void *p )
|
||||
{
|
||||
Address = long(p);
|
||||
Offset = 0;
|
||||
Symbol = "";
|
||||
File = "";
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void output_stack_trace( FILE *out, void **BacktracePointers )
|
||||
{
|
||||
@@ -332,6 +47,10 @@ static void output_stack_trace( FILE *out, void **BacktracePointers )
|
||||
bn.FromAddr( BacktracePointers[i] );
|
||||
bn.Demangle();
|
||||
|
||||
/* Don't show the main module name. */
|
||||
if( bn.File == g_pCrashHandlerArgv0 )
|
||||
bn.File = "";
|
||||
|
||||
if( bn.Symbol == "__libc_start_main" )
|
||||
break;
|
||||
|
||||
|
||||
@@ -4,7 +4,5 @@
|
||||
#define BACKTRACE_MAX_SIZE 1024
|
||||
#define CHILD_MAGIC_PARAMETER "--private-do-crash-handler"
|
||||
|
||||
#define BACKTRACE_METHOD_NOT_AVAILABLE ((void*) -1)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/* RageThreads helpers for threads in Linux, which are based on PIDs and TIDs. */
|
||||
|
||||
#include "global.h"
|
||||
#include "LinuxThreadHelpers.h"
|
||||
#include "RageUtil.h"
|
||||
|
||||
#include "Backtrace.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/unistd.h>
|
||||
#define _LINUX_PTRACE_H // hack to prevent broken linux/ptrace.h from conflicting with sys/ptrace.h
|
||||
#include <linux/user.h>
|
||||
|
||||
/* In Linux, we might be using PID-based or TID-based threads. With PID-based
|
||||
* threads, getpid() returns a unique value for each thread; each thread is a
|
||||
* separate process. Newer kernels using NPTL return the same PID for all
|
||||
* threads; these systems support a gettid() call to get a unique TID for each
|
||||
* thread. */
|
||||
|
||||
|
||||
|
||||
static bool g_bUsingNPTL = false;
|
||||
|
||||
static _syscall0(pid_t,gettid)
|
||||
|
||||
static bool UsingNPTL()
|
||||
{
|
||||
return ThreadsVersion().Left(4) == "NPTL";
|
||||
}
|
||||
|
||||
void InitializePidThreadHelpers()
|
||||
{
|
||||
bool bInitialized = false;
|
||||
if( bInitialized )
|
||||
return;
|
||||
bInitialized = true;
|
||||
|
||||
g_bUsingNPTL = UsingNPTL();
|
||||
}
|
||||
|
||||
/* waitpid(); ThreadID can be a PID or (in NPTL) a TID; doesn't care if the ID
|
||||
* is a clone() or not. */
|
||||
static int waittid( int ThreadID, int *status, int options )
|
||||
{
|
||||
static bool bSupportsWall = true;
|
||||
|
||||
if( bSupportsWall )
|
||||
{
|
||||
int ret = waitpid( ThreadID, status, options | __WALL );
|
||||
if( ret != -1 || errno != EINVAL )
|
||||
return ret;
|
||||
bSupportsWall = false;
|
||||
}
|
||||
|
||||
/* XXX: on 2.2, we need to use __WCLONE only if ThreadID isn't the main thread;
|
||||
* perhaps wait and retry without it if errno == ECHILD? */
|
||||
int ret;
|
||||
ret = waitpid( ThreadID, status, options );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Attempt to PTRACE_ATTACH to a thread, and wait for the SIGSTOP. */
|
||||
static int PtraceAttach( int ThreadID )
|
||||
{
|
||||
int ret;
|
||||
ret = ptrace( PTRACE_ATTACH, ThreadID, NULL, NULL );
|
||||
if( ret == -1 )
|
||||
{
|
||||
printf("ptrace failed: %s\n", strerror(errno) );
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wait for the SIGSTOP from the ptrace call. */
|
||||
int status;
|
||||
ret = waittid( ThreadID, &status, 0 );
|
||||
if( ret == -1 )
|
||||
return -1;
|
||||
|
||||
// printf( "ret %i, exited %i, signalled %i, sig %i, stopped %i, stopsig %i\n", ret, WIFEXITED(status),
|
||||
// WIFSIGNALED(status), WTERMSIG(status), WIFSTOPPED(status), WSTOPSIG(status));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int PtraceDetach( int ThreadID )
|
||||
{
|
||||
return ptrace( PTRACE_DETACH, ThreadID, NULL, NULL );
|
||||
}
|
||||
|
||||
|
||||
CString ThreadsVersion()
|
||||
{
|
||||
#if defined(LINUX)
|
||||
char buf[1024];
|
||||
int ret = confstr( _CS_GNU_LIBPTHREAD_VERSION, buf, sizeof(buf) );
|
||||
RAGE_ASSERT_M( ret != -1, ssprintf( "%i", ret) );
|
||||
return buf;
|
||||
#else
|
||||
return "(unknown)";
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Get this thread's ID (this may be a TID or a PID). */
|
||||
int GetCurrentThreadId()
|
||||
{
|
||||
#if defined(LINUX)
|
||||
InitializePidThreadHelpers(); // for g_bUsingNPTL
|
||||
|
||||
pid_t ret = gettid();
|
||||
|
||||
/* If this fails with ENOSYS, we're on a kernel before gettid. If we
|
||||
* don't have NPTL, then just use getpid(). If we're on NPTL and don't
|
||||
* have gettid(), something's wrong. */
|
||||
if( ret != -1 )
|
||||
return ret;
|
||||
|
||||
ASSERT( !g_bUsingNPTL );
|
||||
#endif
|
||||
|
||||
return getpid();
|
||||
}
|
||||
|
||||
int SuspendThread( int ThreadID )
|
||||
{
|
||||
/*
|
||||
* Linux: We can't simply kill(SIGSTOP) (or tkill), since that will stop all processes
|
||||
* (grr). We can ptrace(PTRACE_ATTACH) the process to stop it, and PTRACE_DETACH
|
||||
* to restart it.
|
||||
*/
|
||||
return PtraceAttach( ThreadID );
|
||||
// kill( ThreadID, SIGSTOP );
|
||||
}
|
||||
|
||||
int ResumeThread( int ThreadID )
|
||||
{
|
||||
return PtraceDetach( ThreadID );
|
||||
// kill( ThreadID, SIGSTOP );
|
||||
}
|
||||
|
||||
|
||||
/* Get user_regs_struct for a thread. tid must not be the current thread.
|
||||
*
|
||||
* tid() is a PID (from getpid) or a TID (from gettid). Note that this may have kernel compatibility
|
||||
* problems, because NPTL is new and its interactions with ptrace() aren't well-defined.
|
||||
* If we're on a non-NPTL system, tid is a regular PID. */
|
||||
int GetThreadContext( int ThreadID, BacktraceContext *ctx )
|
||||
{
|
||||
/* Can't GetThreadContext the current thread. */
|
||||
ASSERT( ThreadID != GetCurrentThreadId() );
|
||||
|
||||
/* Attach to the thread. */
|
||||
int ret = PtraceAttach( ThreadID );
|
||||
|
||||
/* This may fail with EPERM. This can happen for at least two common
|
||||
* reasons: the process might be in a debugger already, or *we* might
|
||||
* already have attached to it via SuspendThread.
|
||||
*
|
||||
* If it's in a debugger, we won't be able to ptrace(PTRACE_GETREGS). If
|
||||
* it's us that attached, we will. Be careful: if SuspendThread was
|
||||
* called to stop the thread (causing this attach to fail), we must not
|
||||
* restart the thread when we're finished. */
|
||||
bool bAttachFailed = (ret == -1);
|
||||
if( bAttachFailed )
|
||||
{
|
||||
RAGE_ASSERT_M( errno == EPERM, ssprintf( "%s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
user_regs_struct regs;
|
||||
ret = ptrace( PTRACE_GETREGS, ThreadID, NULL, ®s );
|
||||
if( ret != 0 )
|
||||
ret = -1;
|
||||
ASSERT( ret == 0 );
|
||||
|
||||
if( !bAttachFailed )
|
||||
{
|
||||
/* We attached to it, so we need to detach. */
|
||||
ret = ptrace( PTRACE_DETACH, ThreadID, NULL, NULL );
|
||||
ASSERT( ret == 0 );
|
||||
}
|
||||
|
||||
|
||||
ctx->eip = regs.eip;
|
||||
ctx->esp = regs.esp;
|
||||
ctx->ebp = regs.ebp;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef PID_THREAD_HELPERS_H
|
||||
#define PID_THREAD_HELPERS_H
|
||||
|
||||
CString ThreadsVersion();
|
||||
|
||||
/* Get the current thread's ThreadID. */
|
||||
int GetCurrentThreadId();
|
||||
|
||||
int SuspendThread( int ThreadID );
|
||||
int ResumeThread( int ThreadID );
|
||||
|
||||
struct BacktraceContext;
|
||||
int GetThreadContext( int ThreadID, BacktraceContext *ctx );
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user