Initial commit.

This commit is contained in:
AJ Kelly
2010-01-26 21:00:30 -06:00
parent 80057f53cd
commit 3e51544930
4074 changed files with 588945 additions and 543956 deletions
+89 -89
View File
@@ -1,89 +1,89 @@
#include "global.h"
#include "RageUtil.h"
#include "RageException.h"
#include "archutils/Unix/EmergencyShutdown.h"
#include <unistd.h>
#include <assert.h>
/* We can define this symbol to catch failed assert() calls. This is only used
* for library code that uses assert(); internally we always use ASSERT, which
* does this for all platforms, not just glibc. */
extern "C" void __assert_fail( const char *assertion, const char *file, unsigned int line, const char *function ) throw()
{
const RString error = ssprintf( "Assertion failure: %s: %s", function, assertion );
#if defined(CRASH_HANDLER)
Checkpoints::SetCheckpoint( file, line, error );
sm_crash( assertion );
#else
/* It'd be nice to just throw an exception here, but throwing an exception
* through C code sometimes explodes. */
DoEmergencyShutdown();
_exit(0);
#endif
}
extern "C" void __assert_perror_fail( int errnum, const char *file, unsigned int line, const char *function ) throw()
{
const RString error = ssprintf( "Assertion failure: %s: %s", function, strerror(errnum) );
#if defined(CRASH_HANDLER)
Checkpoints::SetCheckpoint( file, line, error );
sm_crash( strerror(errnum) );
#else
DoEmergencyShutdown();
_exit(0);
#endif
}
/* Catch unhandled C++ exceptions. Note that this works in g++ even with -fno-exceptions, in
* which case it'll be called if any exceptions are thrown at all. */
#include <cxxabi.h>
void UnexpectedExceptionHandler()
{
type_info *pException = abi::__cxa_current_exception_type();
char const *pName = pException->name();
int iStatus = -1;
char *pDem = abi::__cxa_demangle( pName, 0, 0, &iStatus );
const RString error = ssprintf("Unhandled exception: %s", iStatus? pName:pDem);
#if defined(CRASH_HANDLER)
sm_crash( error );
#endif
}
void InstallExceptionHandler()
{
set_terminate( UnexpectedExceptionHandler );
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "global.h"
#include "RageUtil.h"
#include "RageException.h"
#include "archutils/Unix/EmergencyShutdown.h"
#include <unistd.h>
#include <assert.h>
/* We can define this symbol to catch failed assert() calls. This is only used
* for library code that uses assert(); internally we always use ASSERT, which
* does this for all platforms, not just glibc. */
extern "C" void __assert_fail( const char *assertion, const char *file, unsigned int line, const char *function ) throw()
{
const RString error = ssprintf( "Assertion failure: %s: %s", function, assertion );
#if defined(CRASH_HANDLER)
Checkpoints::SetCheckpoint( file, line, error );
sm_crash( assertion );
#else
/* It'd be nice to just throw an exception here, but throwing an exception
* through C code sometimes explodes. */
DoEmergencyShutdown();
_exit(0);
#endif
}
extern "C" void __assert_perror_fail( int errnum, const char *file, unsigned int line, const char *function ) throw()
{
const RString error = ssprintf( "Assertion failure: %s: %s", function, strerror(errnum) );
#if defined(CRASH_HANDLER)
Checkpoints::SetCheckpoint( file, line, error );
sm_crash( strerror(errnum) );
#else
DoEmergencyShutdown();
_exit(0);
#endif
}
/* Catch unhandled C++ exceptions. Note that this works in g++ even with -fno-exceptions, in
* which case it'll be called if any exceptions are thrown at all. */
#include <cxxabi.h>
void UnexpectedExceptionHandler()
{
type_info *pException = abi::__cxa_current_exception_type();
char const *pName = pException->name();
int iStatus = -1;
char *pDem = abi::__cxa_demangle( pName, 0, 0, &iStatus );
const RString error = ssprintf("Unhandled exception: %s", iStatus? pName:pDem);
#if defined(CRASH_HANDLER)
sm_crash( error );
#endif
}
void InstallExceptionHandler()
{
set_terminate( UnexpectedExceptionHandler );
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+31 -31
View File
@@ -1,31 +1,31 @@
#ifndef ASSERTION_HANDLER_H
#define ASSERTION_HANDLER_H
void InstallExceptionHandler();
#endif
/*
* (c) 2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef ASSERTION_HANDLER_H
#define ASSERTION_HANDLER_H
void InstallExceptionHandler();
#endif
/*
* (c) 2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
File diff suppressed because it is too large Load Diff
+73 -73
View File
@@ -1,73 +1,73 @@
#ifndef BACKTRACE_H
#define BACKTRACE_H
/* This API works like backtrace_pointers(), to retrieve a stack trace. */
#if defined(CPU_PPC)
struct Frame;
#endif
/* This contains the information necessary to backtrace a thread. */
struct BacktraceContext
{
#if defined(CPU_X86) || defined(CPU_X86_64)
const void *ip, *bp, *sp;
#endif
#if defined(UNIX)
pid_t pid;
#endif
#if defined(CPU_PPC)
const Frame *FramePtr;
const void *PC;
#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, const BacktraceContext *ctx = NULL );
/* Set up a BacktraceContext to get a backtrace for a thread. ThreadID may
* not be the current thread. True is returned on success, false on failure. */
bool GetThreadBacktraceContext( uint64_t ThreadID, BacktraceContext *ctx );
/* Set up a BacktraceContext to get a backtrace after receiving a signal, given
* a ucontext_t (see sigaction(2)). (This interface is UNIX-specific.) */
#include <ucontext.h>
void GetSignalBacktraceContext( BacktraceContext *ctx, const ucontext_t *uc );
#define BACKTRACE_METHOD_NOT_AVAILABLE ((void*) -1)
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef BACKTRACE_H
#define BACKTRACE_H
/* This API works like backtrace_pointers(), to retrieve a stack trace. */
#if defined(CPU_PPC)
struct Frame;
#endif
/* This contains the information necessary to backtrace a thread. */
struct BacktraceContext
{
#if defined(CPU_X86) || defined(CPU_X86_64)
const void *ip, *bp, *sp;
#endif
#if defined(UNIX)
pid_t pid;
#endif
#if defined(CPU_PPC)
const Frame *FramePtr;
const void *PC;
#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, const BacktraceContext *ctx = NULL );
/* Set up a BacktraceContext to get a backtrace for a thread. ThreadID may
* not be the current thread. True is returned on success, false on failure. */
bool GetThreadBacktraceContext( uint64_t ThreadID, BacktraceContext *ctx );
/* Set up a BacktraceContext to get a backtrace after receiving a signal, given
* a ucontext_t (see sigaction(2)). (This interface is UNIX-specific.) */
#include <ucontext.h>
void GetSignalBacktraceContext( BacktraceContext *ctx, const ucontext_t *uc );
#define BACKTRACE_METHOD_NOT_AVAILABLE ((void*) -1)
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+486 -486
View File
@@ -1,486 +1,486 @@
/* for dladdr: */
#define __USE_GNU
#include "global.h"
#include "BacktraceNames.h"
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <unistd.h>
#include <cstring>
#include <cerrno>
#include "RageUtil.h"
#if defined(MACOSX)
#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, NULL, NULL, &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\n", status );
break;
}
}
#else
void BacktraceNames::Demangle() { }
#endif
RString BacktraceNames::Format() const
{
RString ShortenedPath = File;
if( ShortenedPath != "" )
{
/* Abbreviate the module name. */
size_t slash = ShortenedPath.rfind('/');
if( slash != ShortenedPath.npos )
ShortenedPath = ShortenedPath.substr(slash+1);
ShortenedPath = RString("(") + ShortenedPath + ")";
}
RString ret = ssprintf( "%0*lx: ", int(sizeof(void*)*2), (long) 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 = (intptr_t) p;
/*
* When calling a function that doesn't return, gcc will truncate a function.
* This results in our return addresses lying just beyond the end of a function.
* Those addresses are correct; our backtrace addresses are usually a list of
* return addresses (that's the same thing you'll see in gdb). dladdr won't work,
* because they're not actually within the bounds of the function.
*
* A generic fix for this would be to adjust the backtrace to be a list of
* places where control was lost, instead of where it'll come back to; to point
* to the CALL instead of just beyond the CALL. That's hard to do generically;
* the stack only contains the return pointers. (Some archs don't even contain
* that: on archs where gcc has to manually store the return address--by pushing
* the IP--it will omit the push when calling a function that never returns.)
*
* Let's try the address we're given, and if that fails to find a symbol, try
* moving up a few bytes. This usually makes "__cxa_throw, std::terminate,
* gsignal" stacks come out right. It probably won't work if there's no space
* between one function and the next, because the first lookup will succeed.
*/
Dl_info di;
if( !dladdr((void *) p, &di) || di.dli_sname == NULL )
{
if( !dladdr( ((char *) p) - 8, &di) )
return;
}
Symbol = di.dli_sname? di.dli_sname:"";
File = di.dli_fname;
Offset = (char*)(p)-(char*)di.dli_saddr;
}
#elif defined(BACKTRACE_LOOKUP_METHOD_DARWIN_DYLD)
/*
Copyright (c) 2002 Jorge Acereda <[email protected]> &
Peter O'Gorman <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Cleaned and adapted. -glenn
*/
#include <mach-o/dyld.h>
#include <mach-o/nlist.h>
static const load_command *next_load_command( const load_command *cmd )
{
const char *p = (const char *) cmd;
p += cmd->cmdsize;
return (load_command *) p;
}
/* Return the image index of the given pointer, or -1 if not found. */
static int osx_find_image( const void *p )
{
unsigned long image_count = _dyld_image_count();
for( unsigned i = 0; i < image_count; i++ )
{
const struct mach_header *header = _dyld_get_image_header(i);
if( header == NULL )
continue;
/* The load commands directly follow the mach_header. */
const struct load_command *cmd = (struct load_command *) &header[1];
for( unsigned j = 0; j < header->ncmds; j++ )
{
/* We only care about mapped segments. */
if( cmd->cmd != LC_SEGMENT )
{
cmd = next_load_command(cmd);
continue;
}
const segment_command *scmd = (const segment_command *) cmd;
const unsigned long bottom = scmd->vmaddr + _dyld_get_image_vmaddr_slide( i );
const unsigned long top = scmd->vmaddr + scmd->vmsize + _dyld_get_image_vmaddr_slide( i );
if ( (unsigned long) p >= bottom && (unsigned long) p < top )
return i;
cmd = next_load_command(cmd);
}
}
return -1;
}
static const char *osx_find_link_edit( const struct mach_header *header )
{
const struct load_command *cmd = (struct load_command *) &header[1];
for( unsigned i = 0; i < header->ncmds; i++, cmd = next_load_command(cmd) )
{
if( cmd->cmd != LC_SEGMENT )
continue;
const segment_command *scmd = (const segment_command *) cmd;
if( !strcmp(scmd->segname, "__LINKEDIT") )
return (char *) ( scmd->vmaddr - scmd->fileoff );
}
return NULL;
}
void BacktraceNames::FromAddr( const void *p )
{
Address = (intptr_t) p;
/* Find the image with the given pointer. */
int index = osx_find_image( p );
if( index == -1 )
return;
File = _dyld_get_image_name( index );
/* Find the link-edit pointer. */
const char *link_edit = osx_find_link_edit( _dyld_get_image_header(index) );
if( link_edit == NULL )
return;
link_edit += _dyld_get_image_vmaddr_slide( index );
unsigned long addr = (unsigned long)p - _dyld_get_image_vmaddr_slide(index);
const struct mach_header *header = _dyld_get_image_header( index );
const struct load_command *cmd = (struct load_command *) &header[1];
unsigned long diff = 0xffffffff;
const char *dli_sname = NULL;
void *dli_saddr = NULL;
for( unsigned long i = 0; i < header->ncmds; i++, cmd = next_load_command(cmd) )
{
if( cmd->cmd != LC_SYMTAB )
continue;
const symtab_command *scmd = (const symtab_command *) cmd;
struct nlist *symtable = (struct nlist *)( link_edit + scmd->symoff );
for( unsigned long j = 0; j < scmd->nsyms; j++ )
{
if( !symtable[j].n_value )
continue; /* Undefined */
if( symtable[j].n_type >= N_PEXT )
continue; /* Debug symbol */
if( addr >= symtable[j].n_value && diff >= (addr - symtable[j].n_value) )
{
diff = addr - (unsigned long)symtable[j].n_value;
dli_saddr = symtable[j].n_value + ((char *)p - addr);
dli_sname = (const char *)( link_edit + scmd->stroff + symtable[j].n_un.n_strx );
}
}
}
if( diff == 0xffffffff )
return;
Symbol = dli_sname;
Offset = (char*)(p)-(char*)dli_saddr;
/*
* __start -> _start
* __ZN7RageLog5TraceEPKcz -> _ZN7RageLog5TraceEPKcz (so demangling will work)
*/
if( Symbol.Left(1) == "_" )
Symbol = Symbol.substr(1);
/* After stripping off the leading _
* _GLOBAL__I__ZN5ModelC2Ev -> _ZN5ModelC2Ev
* _GLOBAL__D__Z12ForceToAsciiR7CStdStrIcE -> _Z12ForceToAsciiR7CStdStrIcE
*/
if( Symbol.Left(9) == "_GLOBAL__" )
Symbol = Symbol.substr(11);
}
#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 = (intptr_t) p;
char **foo = backtrace_symbols(&p, 1);
if( foo == NULL )
return;
FromString( foo[0] );
free(foo);
}
/* "path(mangled name+offset) [address]" */
void BacktraceNames::FromString( RString s )
{
/* Hacky parser. I don't want to use regexes in the crash handler. */
RString MangledAndOffset, sAddress;
unsigned pos = 0;
while( pos < s.size() && s[pos] != '(' && s[pos] != '[' )
File += s[pos++];
Trim( File );
if( pos < s.size() && s[pos] == '(' )
{
pos++;
while( pos < s.size() && s[pos] != ')' )
MangledAndOffset += s[pos++];
}
if( MangledAndOffset != "" )
{
size_t plus = MangledAndOffset.rfind('+');
if(plus == MangledAndOffset.npos)
{
Symbol = MangledAndOffset;
Offset = 0;
}
else
{
Symbol = MangledAndOffset.substr(0, plus);
RString 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];
pid_t pid;
pid_t ppid = getpid(); /* Do this before fork()ing! */
Offset = 0;
Address = intptr_t(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], fileno(stdout));
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);
_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;
}
vector<RString> 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];
size_t pos = File.find('(');
size_t 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];
size_t pos = File.find('(');
size_t 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 = intptr_t(p);
Offset = 0;
Symbol = "";
File = "";
}
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/* for dladdr: */
#define __USE_GNU
#include "global.h"
#include "BacktraceNames.h"
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <unistd.h>
#include <cstring>
#include <cerrno>
#include "RageUtil.h"
#if defined(MACOSX)
#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, NULL, NULL, &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\n", status );
break;
}
}
#else
void BacktraceNames::Demangle() { }
#endif
RString BacktraceNames::Format() const
{
RString ShortenedPath = File;
if( ShortenedPath != "" )
{
/* Abbreviate the module name. */
size_t slash = ShortenedPath.rfind('/');
if( slash != ShortenedPath.npos )
ShortenedPath = ShortenedPath.substr(slash+1);
ShortenedPath = RString("(") + ShortenedPath + ")";
}
RString ret = ssprintf( "%0*lx: ", int(sizeof(void*)*2), (long) 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 = (intptr_t) p;
/*
* When calling a function that doesn't return, gcc will truncate a function.
* This results in our return addresses lying just beyond the end of a function.
* Those addresses are correct; our backtrace addresses are usually a list of
* return addresses (that's the same thing you'll see in gdb). dladdr won't work,
* because they're not actually within the bounds of the function.
*
* A generic fix for this would be to adjust the backtrace to be a list of
* places where control was lost, instead of where it'll come back to; to point
* to the CALL instead of just beyond the CALL. That's hard to do generically;
* the stack only contains the return pointers. (Some archs don't even contain
* that: on archs where gcc has to manually store the return address--by pushing
* the IP--it will omit the push when calling a function that never returns.)
*
* Let's try the address we're given, and if that fails to find a symbol, try
* moving up a few bytes. This usually makes "__cxa_throw, std::terminate,
* gsignal" stacks come out right. It probably won't work if there's no space
* between one function and the next, because the first lookup will succeed.
*/
Dl_info di;
if( !dladdr((void *) p, &di) || di.dli_sname == NULL )
{
if( !dladdr( ((char *) p) - 8, &di) )
return;
}
Symbol = di.dli_sname? di.dli_sname:"";
File = di.dli_fname;
Offset = (char*)(p)-(char*)di.dli_saddr;
}
#elif defined(BACKTRACE_LOOKUP_METHOD_DARWIN_DYLD)
/*
Copyright (c) 2002 Jorge Acereda <[email protected]> &
Peter O'Gorman <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Cleaned and adapted. -glenn
*/
#include <mach-o/dyld.h>
#include <mach-o/nlist.h>
static const load_command *next_load_command( const load_command *cmd )
{
const char *p = (const char *) cmd;
p += cmd->cmdsize;
return (load_command *) p;
}
/* Return the image index of the given pointer, or -1 if not found. */
static int osx_find_image( const void *p )
{
unsigned long image_count = _dyld_image_count();
for( unsigned i = 0; i < image_count; i++ )
{
const struct mach_header *header = _dyld_get_image_header(i);
if( header == NULL )
continue;
/* The load commands directly follow the mach_header. */
const struct load_command *cmd = (struct load_command *) &header[1];
for( unsigned j = 0; j < header->ncmds; j++ )
{
/* We only care about mapped segments. */
if( cmd->cmd != LC_SEGMENT )
{
cmd = next_load_command(cmd);
continue;
}
const segment_command *scmd = (const segment_command *) cmd;
const unsigned long bottom = scmd->vmaddr + _dyld_get_image_vmaddr_slide( i );
const unsigned long top = scmd->vmaddr + scmd->vmsize + _dyld_get_image_vmaddr_slide( i );
if ( (unsigned long) p >= bottom && (unsigned long) p < top )
return i;
cmd = next_load_command(cmd);
}
}
return -1;
}
static const char *osx_find_link_edit( const struct mach_header *header )
{
const struct load_command *cmd = (struct load_command *) &header[1];
for( unsigned i = 0; i < header->ncmds; i++, cmd = next_load_command(cmd) )
{
if( cmd->cmd != LC_SEGMENT )
continue;
const segment_command *scmd = (const segment_command *) cmd;
if( !strcmp(scmd->segname, "__LINKEDIT") )
return (char *) ( scmd->vmaddr - scmd->fileoff );
}
return NULL;
}
void BacktraceNames::FromAddr( const void *p )
{
Address = (intptr_t) p;
/* Find the image with the given pointer. */
int index = osx_find_image( p );
if( index == -1 )
return;
File = _dyld_get_image_name( index );
/* Find the link-edit pointer. */
const char *link_edit = osx_find_link_edit( _dyld_get_image_header(index) );
if( link_edit == NULL )
return;
link_edit += _dyld_get_image_vmaddr_slide( index );
unsigned long addr = (unsigned long)p - _dyld_get_image_vmaddr_slide(index);
const struct mach_header *header = _dyld_get_image_header( index );
const struct load_command *cmd = (struct load_command *) &header[1];
unsigned long diff = 0xffffffff;
const char *dli_sname = NULL;
void *dli_saddr = NULL;
for( unsigned long i = 0; i < header->ncmds; i++, cmd = next_load_command(cmd) )
{
if( cmd->cmd != LC_SYMTAB )
continue;
const symtab_command *scmd = (const symtab_command *) cmd;
struct nlist *symtable = (struct nlist *)( link_edit + scmd->symoff );
for( unsigned long j = 0; j < scmd->nsyms; j++ )
{
if( !symtable[j].n_value )
continue; /* Undefined */
if( symtable[j].n_type >= N_PEXT )
continue; /* Debug symbol */
if( addr >= symtable[j].n_value && diff >= (addr - symtable[j].n_value) )
{
diff = addr - (unsigned long)symtable[j].n_value;
dli_saddr = symtable[j].n_value + ((char *)p - addr);
dli_sname = (const char *)( link_edit + scmd->stroff + symtable[j].n_un.n_strx );
}
}
}
if( diff == 0xffffffff )
return;
Symbol = dli_sname;
Offset = (char*)(p)-(char*)dli_saddr;
/*
* __start -> _start
* __ZN7RageLog5TraceEPKcz -> _ZN7RageLog5TraceEPKcz (so demangling will work)
*/
if( Symbol.Left(1) == "_" )
Symbol = Symbol.substr(1);
/* After stripping off the leading _
* _GLOBAL__I__ZN5ModelC2Ev -> _ZN5ModelC2Ev
* _GLOBAL__D__Z12ForceToAsciiR7CStdStrIcE -> _Z12ForceToAsciiR7CStdStrIcE
*/
if( Symbol.Left(9) == "_GLOBAL__" )
Symbol = Symbol.substr(11);
}
#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 = (intptr_t) p;
char **foo = backtrace_symbols(&p, 1);
if( foo == NULL )
return;
FromString( foo[0] );
free(foo);
}
/* "path(mangled name+offset) [address]" */
void BacktraceNames::FromString( RString s )
{
/* Hacky parser. I don't want to use regexes in the crash handler. */
RString MangledAndOffset, sAddress;
unsigned pos = 0;
while( pos < s.size() && s[pos] != '(' && s[pos] != '[' )
File += s[pos++];
Trim( File );
if( pos < s.size() && s[pos] == '(' )
{
pos++;
while( pos < s.size() && s[pos] != ')' )
MangledAndOffset += s[pos++];
}
if( MangledAndOffset != "" )
{
size_t plus = MangledAndOffset.rfind('+');
if(plus == MangledAndOffset.npos)
{
Symbol = MangledAndOffset;
Offset = 0;
}
else
{
Symbol = MangledAndOffset.substr(0, plus);
RString 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];
pid_t pid;
pid_t ppid = getpid(); /* Do this before fork()ing! */
Offset = 0;
Address = intptr_t(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], fileno(stdout));
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);
_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;
}
vector<RString> 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];
size_t pos = File.find('(');
size_t 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];
size_t pos = File.find('(');
size_t 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 = intptr_t(p);
Offset = 0;
Symbol = "";
File = "";
}
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+41 -41
View File
@@ -1,41 +1,41 @@
#ifndef BACKTRACE_NAMES_H
#define BACKTRACE_NAMES_H
struct BacktraceNames
{
RString Symbol, File;
intptr_t Address;
int Offset;
void FromAddr( const void *p );
void FromString( RString str );
void Demangle();
RString Format() const;
BacktraceNames(): Address(0), Offset(0) { }
};
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef BACKTRACE_NAMES_H
#define BACKTRACE_NAMES_H
struct BacktraceNames
{
RString Symbol, File;
intptr_t Address;
int Offset;
void FromAddr( const void *p );
void FromString( RString str );
void Demangle();
RString Format() const;
BacktraceNames(): Address(0), Offset(0) { }
};
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+452 -450
View File
@@ -1,450 +1,452 @@
#include "global.h"
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <unistd.h>
#include <cerrno>
#include <limits.h>
#include <fcntl.h>
#include <csignal>
#include "RageLog.h" /* for RageLog::GetAdditionalLog, etc, only */
#include "RageThreads.h"
#include "Backtrace.h"
#include <sys/types.h>
#include <sys/wait.h>
#include "CrashHandler.h"
#include "CrashHandlerInternal.h"
extern uint64_t GetInvalidThreadId();
extern const char *g_pCrashHandlerArgv0;
static void safe_print( int fd, ... )
{
va_list ap;
va_start( ap, fd );
while( true )
{
const char *p = va_arg( ap, const char * );
if( p == NULL )
break;
size_t len = strlen( p );
while( len )
{
ssize_t result = write( fd, p, strlen(p) );
if( result == -1 )
_exit( 1 );
len -= result;
p += result;
}
}
va_end( ap );
}
#if defined(LINUX)
static void GetExecutableName( char *buf, int bufsize )
{
/* readlink(/proc/pid/exe). This is more reliable than argv[0]. */
char proc_fn[1024] = "/proc/";
strcat( proc_fn, itoa( getpid() ) );
strcat( proc_fn, "/exe" );
int got = readlink( proc_fn, buf, bufsize-1 );
if( got == -1 )
{
safe_print( fileno(stderr), "Crash handler readlink ", proc_fn, " failed: ", strerror(errno), "\n", NULL );
strncpy( buf, g_pCrashHandlerArgv0, bufsize );
buf[bufsize-1] = 0;
}
buf[got] = 0;
}
#else
static void GetExecutableName( char *buf, int bufsize )
{
strncpy( buf, g_pCrashHandlerArgv0, bufsize );
buf[bufsize-1] = 0;
}
#endif
static void NORETURN spawn_child_process( int from_parent )
{
/* We need to re-exec ourself, to get a clean process. Close all
* FDs except for 0-2 and to_child, and then assign to_child to 3. */
for( int fd = 3; fd < 1024; ++fd )
if( fd != from_parent ) close(fd);
if( from_parent != 3 )
{
dup2( from_parent, 3 );
close( from_parent );
}
char path[1024];
char magic[32];
GetExecutableName( path, sizeof(path) );
strncpy( magic, CHILD_MAGIC_PARAMETER, sizeof(magic) );
/* Use execve; it's the lowest-level of the exec calls. The others may allocate. */
char *argv[3] = { path, magic, NULL };
char *envp[1] = { NULL };
execve( path, argv, envp );
/* If we got here, the exec failed. We can't call strerror. */
// safe_print(fileno(stderr), "Crash handler execl(", path, ") failed: ", strerror(errno), "\n", NULL);
safe_print( fileno(stderr), "Crash handler execl(", path, ") failed: ", itoa( errno ), "\n", NULL );
_exit(1);
}
/* write(), but retry a couple times on EINTR. */
static int retried_write( int fd, const void *buf, size_t count )
{
int tries = 3, ret;
do
{
ret = write( fd, buf, count );
}
while( ret == -1 && errno == EINTR && tries-- );
return ret;
}
static bool parent_write( int to_child, const void *p, size_t size )
{
int ret = retried_write( to_child, p, size );
if( ret == -1 )
{
safe_print( fileno(stderr), "Unexpected write() result (", strerror(errno), ")\n", NULL );
return false;
}
if( size_t(ret) != size )
{
safe_print( fileno(stderr), "Unexpected write() result (", itoa(ret), ")\n", NULL );
return false;
}
return true;
}
static void parent_process( int to_child, const CrashData *crash )
{
/* 1. Write the CrashData. */
if( !parent_write(to_child, crash, sizeof(CrashData)) )
return;
/* 2. Write info. */
const char *p = RageLog::GetInfo();
int size = strlen( p )+1;
if( !parent_write(to_child, &size, sizeof(size)) )
return;
if( !parent_write(to_child, p, size) )
return;
/* 3. Write AdditionalLog. */
p = RageLog::GetAdditionalLog();
size = strlen( p )+1;
if( !parent_write(to_child, &size, sizeof(size)) )
return;
if( !parent_write(to_child, p, size) )
return;
/* 4. Write RecentLogs. */
int cnt = 0;
const char *ps[1024];
while( cnt < 1024 && (ps[cnt] = RageLog::GetRecentLog( cnt )) != NULL )
++cnt;
parent_write(to_child, &cnt, sizeof(cnt));
for( int i = 0; i < cnt; ++i )
{
size = strlen(ps[i])+1;
if( !parent_write(to_child, &size, sizeof(size)) )
return;
if( !parent_write(to_child, ps[i], size) )
return;
}
/* 5. Write CHECKPOINTs. */
static char buf[1024*32];
Checkpoints::GetLogs( buf, sizeof(buf), "\n" );
size = strlen( buf )+1;
if( !parent_write(to_child, &size, sizeof(size)) )
return;
if( !parent_write(to_child, buf, size) )
return;
/* 6. Write the crashed thread's name. */
p = RageThread::GetCurrentThreadName();
size = strlen( p )+1;
if( !parent_write(to_child, &size, sizeof(size)) )
return;
if( !parent_write(to_child, p, size) )
return;
}
/* The parent process is the crashed process. It'll send data to the
* child, who will do stuff with it. The parent then waits for the
* child to quit, and exits.
*
* We can do whatever fancy things we want in the child process. However,
* let's not open any windows until we at least try to shut down OpenGL,
* since it may cause problems. We don't want to try to shut down OpenGL
* until we've sent all of our data, since it might explode.
*
* So, first fork off the error reporting child, send data to it, shut down
* OpenGL, close the socket and wait for the child to shut down.
*
* The child reads the data from the parent, waits for the socket to close
* (EOF), and it's then free to open windows and stuff.
*
* XXX: make sure the parent dying doesn't take out the child
*/
/* The x86 backtrace() in glibc doesn't make any effort at all to decode
* signal trampolines. The result is that it doesn't properly show the
* function that actually caused the signal--which is the most important
* one! So, we have to do it all ourself. */
static void RunCrashHandler( const CrashData *crash )
{
if( g_pCrashHandlerArgv0 == NULL )
{
safe_print( fileno(stderr), "Crash handler failed: CrashHandlerHandleArgs was not called\n", NULL );
_exit( 1 );
}
/* Block SIGPIPE, so we get EPIPE. */
struct sigaction sa;
memset( &sa, 0, sizeof(sa) );
sa.sa_handler = SIG_IGN;
if( sigaction( SIGPIPE, &sa, NULL ) != 0 )
{
safe_print( fileno(stderr), "sigaction() failed: %s", strerror(errno), NULL );
/* non-fatal */
}
static int received = 0;
static int active = 0;
if( active )
{
/* We've received a second signal. This may mean that another thread
* crashed before we stopped it, or it may mean that the crash handler
* crashed. */
switch( crash->type )
{
case CrashData::SIGNAL:
safe_print( fileno(stderr), "Fatal signal (", SignalName(crash->signal), ")", NULL );
break;
case CrashData::FORCE_CRASH:
safe_print( fileno(stderr), "Crash handler failed: \"", crash->reason, "\"", NULL );
break;
default:
safe_print( fileno(stderr), "Unexpected RunCrashHandler call (", itoa(crash->type), ")", NULL );
break;
}
if( active == 1 )
safe_print( fileno(stderr), " while still in the crash handler\n", NULL);
else if( active == 2 )
safe_print( fileno(stderr), " while in the crash handler child\n", NULL);
_exit( 1 );
}
active = 1;
received = getpid();
/* Stop other threads. XXX: This prints a spurious ptrace error if any threads
* are already suspended, which happens in ForceCrashHandlerDeadlock(). */
RageThread::HaltAllThreads();
/* 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];
if( pipe(fds) != 0 )
{
safe_print( fileno(stderr), "Crash handler pipe() failed: ", strerror(errno), "\n", NULL );
exit( 1 );
}
pid_t childpid = fork();
if( childpid == -1 )
{
safe_print( fileno(stderr), "Crash handler fork() failed: ", strerror(errno), "\n", NULL );
_exit( 1 );
}
if( childpid == 0 )
{
active = 2;
close( fds[1] );
spawn_child_process( fds[0] );
}
else
{
close( fds[0] );
parent_process( fds[1], crash );
close( fds[1] );
int status = 0;
#if !defined(MACOSX)
waitpid( childpid, &status, 0 );
#endif
/* We need to resume threads before continuing, or we may deadlock on _exit(). */
/* XXX: race condition: If two threads are deadlocked on a pair of mutexes, there's
* a chance that they'll both try to lock the other's mutex at the same time. If
* that happens, then they'll both time out the lock at about the same time. One
* will trigger the deadlock crash first, and the other will be suspended. However,
* once we resume it here, it'll continue, and * trigger the deadlock crash again.
* It can result in unrecoverable deadlocks. */
RageThread::ResumeAllThreads();
if( WIFSIGNALED(status) )
safe_print( fileno(stderr), "Crash handler child exited with signal ", itoa(WTERMSIG(status)), "\n", NULL );
}
}
static void BacktraceAllThreads( CrashData& crash )
{
int iCnt = 1;
uint64_t iID;
for( int i = 0; RageThread::EnumThreadIDs(i, iID); ++i )
{
if( iID == GetInvalidThreadId() || iID == RageThread::GetCurrentThreadID() )
continue;
BacktraceContext ctx;
if( GetThreadBacktraceContext( iID, &ctx ) )
GetBacktrace( crash.BacktracePointers[iCnt], BACKTRACE_MAX_SIZE, &ctx );
strncpy( crash.m_ThreadName[iCnt], RageThread::GetThreadNameByID(iID), sizeof(crash.m_ThreadName[0])-1 );
++iCnt;
if( iCnt == CrashData::MAX_BACKTRACE_THREADS )
break;
}
}
void CrashHandler::ForceCrash( const char *reason )
{
CrashData crash;
memset( &crash, 0, sizeof(crash) );
crash.type = CrashData::FORCE_CRASH;
strncpy( crash.reason, reason, sizeof(crash.reason) );
crash.reason[ sizeof(crash.reason)-1 ] = 0;
GetBacktrace( crash.BacktracePointers[0], BACKTRACE_MAX_SIZE, NULL );
RunCrashHandler( &crash );
}
void CrashHandler::ForceDeadlock( RString reason, uint64_t iID )
{
CrashData crash;
memset( &crash, 0, sizeof(crash) );
crash.type = CrashData::FORCE_CRASH;
GetBacktrace( crash.BacktracePointers[0], BACKTRACE_MAX_SIZE, NULL );
if( iID == GetInvalidThreadId() )
{
/* Backtrace all threads. */
BacktraceAllThreads( crash );
}
else
{
BacktraceContext ctx;
if( !GetThreadBacktraceContext( iID, &ctx ) )
reason += "; GetThreadBacktraceContext failed";
else
GetBacktrace( crash.BacktracePointers[1], BACKTRACE_MAX_SIZE, &ctx );
strncpy( crash.m_ThreadName[1], RageThread::GetThreadNameByID(iID), sizeof(crash.m_ThreadName[0])-1 );
}
strncpy( crash.m_ThreadName[0], RageThread::GetCurrentThreadName(), sizeof(crash.m_ThreadName[0])-1 );
strncpy( crash.reason, reason, min(sizeof(crash.reason) - 1, reason.length()) );
crash.reason[ sizeof(crash.reason)-1 ] = 0;
RunCrashHandler( &crash );
_exit( 1 );
}
void CrashHandler::CrashSignalHandler( int signal, siginfo_t *si, const ucontext_t *uc )
{
static bool bInCrashSignalHandler = false;
if( bInCrashSignalHandler )
{
fprintf(stderr, "Fatal: crash from within the crash signal handler\n");
_exit(1);
}
bInCrashSignalHandler = true;
CrashData crash;
memset( &crash, 0, sizeof(crash) );
crash.type = CrashData::SIGNAL;
crash.signal = signal;
crash.si = *si;
BacktraceContext ctx;
GetSignalBacktraceContext( &ctx, uc );
GetBacktrace( crash.BacktracePointers[0], BACKTRACE_MAX_SIZE, &ctx );
#if defined(HAVE_DECL_SIGUSR1) && HAVE_DECL_SIGUSR1
if( signal == SIGUSR1 )
BacktraceAllThreads( crash );
#endif
strncpy( crash.m_ThreadName[0], RageThread::GetCurrentThreadName(), sizeof(crash.m_ThreadName[0])-1 );
bInCrashSignalHandler = false;
/* RunCrashHandler handles any recursive crashes of its own by itself. */
RunCrashHandler( &crash );
}
void CrashHandler::InitializeCrashHandler()
{
InitializeBacktrace();
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "global.h"
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <unistd.h>
#include <cerrno>
#include <limits.h>
#include <fcntl.h>
#include <csignal>
#include "RageLog.h" /* for RageLog::GetAdditionalLog, etc, only */
#include "RageThreads.h"
#include "Backtrace.h"
#include <sys/types.h>
#include <sys/wait.h>
#include "CrashHandler.h"
#include "CrashHandlerInternal.h"
extern uint64_t GetInvalidThreadId();
extern const char *g_pCrashHandlerArgv0;
static void safe_print( int fd, ... )
{
va_list ap;
va_start( ap, fd );
while( true )
{
const char *p = va_arg( ap, const char * );
if( p == NULL )
break;
size_t len = strlen( p );
while( len )
{
ssize_t result = write( fd, p, strlen(p) );
if( result == -1 )
_exit( 1 );
len -= result;
p += result;
}
}
va_end( ap );
}
#if defined(LINUX)
static void GetExecutableName( char *buf, int bufsize )
{
/* readlink(/proc/pid/exe). This is more reliable than argv[0]. */
char proc_fn[1024] = "/proc/";
strcat( proc_fn, itoa( getpid() ) );
strcat( proc_fn, "/exe" );
int got = readlink( proc_fn, buf, bufsize-1 );
if( got == -1 )
{
safe_print( fileno(stderr), "Crash handler readlink ", proc_fn, " failed: ", strerror(errno), "\n", NULL );
strncpy( buf, g_pCrashHandlerArgv0, bufsize );
buf[bufsize-1] = 0;
}
buf[got] = 0;
}
#else
static void GetExecutableName( char *buf, int bufsize )
{
strncpy( buf, g_pCrashHandlerArgv0, bufsize );
buf[bufsize-1] = 0;
}
#endif
static void NORETURN spawn_child_process( int from_parent )
{
/* We need to re-exec ourself, to get a clean process. Close all
* FDs except for 0-2 and to_child, and then assign to_child to 3. */
for( int fd = 3; fd < 1024; ++fd )
if( fd != from_parent ) close(fd);
if( from_parent != 3 )
{
dup2( from_parent, 3 );
close( from_parent );
}
char path[1024];
char magic[32];
GetExecutableName( path, sizeof(path) );
strncpy( magic, CHILD_MAGIC_PARAMETER, sizeof(magic) );
/* Use execve; it's the lowest-level of the exec calls. The others may allocate. */
char *argv[3] = { path, magic, NULL };
char *envp[1] = { NULL };
execve( path, argv, envp );
/* If we got here, the exec failed. We can't call strerror. */
// safe_print(fileno(stderr), "Crash handler execl(", path, ") failed: ", strerror(errno), "\n", NULL);
safe_print( fileno(stderr), "Crash handler execl(", path, ") failed: ", itoa( errno ), "\n", NULL );
_exit(1);
}
/* write(), but retry a couple times on EINTR. */
static int retried_write( int fd, const void *buf, size_t count )
{
int tries = 3, ret;
do
{
ret = write( fd, buf, count );
}
while( ret == -1 && errno == EINTR && tries-- );
return ret;
}
static bool parent_write( int to_child, const void *p, size_t size )
{
int ret = retried_write( to_child, p, size );
if( ret == -1 )
{
safe_print( fileno(stderr), "Unexpected write() result (", strerror(errno), ")\n", NULL );
return false;
}
if( size_t(ret) != size )
{
safe_print( fileno(stderr), "Unexpected write() result (", itoa(ret), ")\n", NULL );
return false;
}
return true;
}
static void parent_process( int to_child, const CrashData *crash )
{
/* 1. Write the CrashData. */
if( !parent_write(to_child, crash, sizeof(CrashData)) )
return;
/* 2. Write info. */
const char *p = RageLog::GetInfo();
int size = strlen( p )+1;
if( !parent_write(to_child, &size, sizeof(size)) )
return;
if( !parent_write(to_child, p, size) )
return;
/* 3. Write AdditionalLog. */
p = RageLog::GetAdditionalLog();
size = strlen( p )+1;
if( !parent_write(to_child, &size, sizeof(size)) )
return;
if( !parent_write(to_child, p, size) )
return;
/* 4. Write RecentLogs. */
int cnt = 0;
const char *ps[1024];
while( cnt < 1024 && (ps[cnt] = RageLog::GetRecentLog( cnt )) != NULL )
++cnt;
parent_write(to_child, &cnt, sizeof(cnt));
for( int i = 0; i < cnt; ++i )
{
size = strlen(ps[i])+1;
if( !parent_write(to_child, &size, sizeof(size)) )
return;
if( !parent_write(to_child, ps[i], size) )
return;
}
/* 5. Write CHECKPOINTs. */
static char buf[1024*32];
Checkpoints::GetLogs( buf, sizeof(buf), "\n" );
size = strlen( buf )+1;
if( !parent_write(to_child, &size, sizeof(size)) )
return;
if( !parent_write(to_child, buf, size) )
return;
/* 6. Write the crashed thread's name. */
p = RageThread::GetCurrentThreadName();
size = strlen( p )+1;
if( !parent_write(to_child, &size, sizeof(size)) )
return;
if( !parent_write(to_child, p, size) )
return;
}
/* The parent process is the crashed process. It'll send data to the
* child, who will do stuff with it. The parent then waits for the
* child to quit, and exits.
*
* We can do whatever fancy things we want in the child process. However,
* let's not open any windows until we at least try to shut down OpenGL,
* since it may cause problems. We don't want to try to shut down OpenGL
* until we've sent all of our data, since it might explode.
*
* So, first fork off the error reporting child, send data to it, shut down
* OpenGL, close the socket and wait for the child to shut down.
*
* The child reads the data from the parent, waits for the socket to close
* (EOF), and it's then free to open windows and stuff.
*
* XXX: make sure the parent dying doesn't take out the child
*/
/* The x86 backtrace() in glibc doesn't make any effort at all to decode
* signal trampolines. The result is that it doesn't properly show the
* function that actually caused the signal--which is the most important
* one! So, we have to do it all ourself. */
static void RunCrashHandler( const CrashData *crash )
{
if( g_pCrashHandlerArgv0 == NULL )
{
safe_print( fileno(stderr), "Crash handler failed: CrashHandlerHandleArgs was not called\n", NULL );
_exit( 1 );
}
/* Block SIGPIPE, so we get EPIPE. */
struct sigaction sa;
memset( &sa, 0, sizeof(sa) );
sa.sa_handler = SIG_IGN;
if( sigaction( SIGPIPE, &sa, NULL ) != 0 )
{
safe_print( fileno(stderr), "sigaction() failed: %s", strerror(errno), NULL );
/* non-fatal */
}
static int received = 0;
static int active = 0;
if( active )
{
/* We've received a second signal. This may mean that another thread
* crashed before we stopped it, or it may mean that the crash handler
* crashed. */
switch( crash->type )
{
case CrashData::SIGNAL:
safe_print( fileno(stderr), "Fatal signal (", SignalName(crash->signal), ")", NULL );
break;
case CrashData::FORCE_CRASH:
safe_print( fileno(stderr), "Crash handler failed: \"", crash->reason, "\"", NULL );
break;
default:
safe_print( fileno(stderr), "Unexpected RunCrashHandler call (", itoa(crash->type), ")", NULL );
break;
}
if( active == 1 )
safe_print( fileno(stderr), " while still in the crash handler\n", NULL);
else if( active == 2 )
safe_print( fileno(stderr), " while in the crash handler child\n", NULL);
_exit( 1 );
}
active = 1;
received = getpid();
/* Stop other threads. XXX: This prints a spurious ptrace error if any threads
* are already suspended, which happens in ForceCrashHandlerDeadlock(). */
RageThread::HaltAllThreads();
/* 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];
if( pipe(fds) != 0 )
{
safe_print( fileno(stderr), "Crash handler pipe() failed: ", strerror(errno), "\n", NULL );
exit( 1 );
}
pid_t childpid = fork();
if( childpid == -1 )
{
safe_print( fileno(stderr), "Crash handler fork() failed: ", strerror(errno), "\n", NULL );
_exit( 1 );
}
if( childpid == 0 )
{
active = 2;
close( fds[1] );
spawn_child_process( fds[0] );
}
else
{
close( fds[0] );
parent_process( fds[1], crash );
close( fds[1] );
int status = 0;
#if !defined(MACOSX)
waitpid( childpid, &status, 0 );
#endif
/* We need to resume threads before continuing, or we may deadlock on _exit(). */
/* XXX: race condition: If two threads are deadlocked on a pair of mutexes, there's
* a chance that they'll both try to lock the other's mutex at the same time. If
* that happens, then they'll both time out the lock at about the same time. One
* will trigger the deadlock crash first, and the other will be suspended. However,
* once we resume it here, it'll continue, and * trigger the deadlock crash again.
* It can result in unrecoverable deadlocks. */
RageThread::ResumeAllThreads();
if( WIFSIGNALED(status) )
safe_print( fileno(stderr), "Crash handler child exited with signal ", itoa(WTERMSIG(status)), "\n", NULL );
}
}
static void BacktraceAllThreads( CrashData& crash )
{
int iCnt = 1;
uint64_t iID;
for( int i = 0; RageThread::EnumThreadIDs(i, iID); ++i )
{
if( iID == GetInvalidThreadId() || iID == RageThread::GetCurrentThreadID() )
continue;
BacktraceContext ctx;
if( GetThreadBacktraceContext( iID, &ctx ) )
GetBacktrace( crash.BacktracePointers[iCnt], BACKTRACE_MAX_SIZE, &ctx );
strncpy( crash.m_ThreadName[iCnt], RageThread::GetThreadNameByID(iID), sizeof(crash.m_ThreadName[0])-1 );
++iCnt;
if( iCnt == CrashData::MAX_BACKTRACE_THREADS )
break;
}
}
void CrashHandler::ForceCrash( const char *reason )
{
CrashData crash;
memset( &crash, 0, sizeof(crash) );
crash.type = CrashData::FORCE_CRASH;
strncpy( crash.reason, reason, sizeof(crash.reason) );
crash.reason[ sizeof(crash.reason)-1 ] = 0;
GetBacktrace( crash.BacktracePointers[0], BACKTRACE_MAX_SIZE, NULL );
RunCrashHandler( &crash );
}
void CrashHandler::ForceDeadlock( RString reason, uint64_t iID )
{
CrashData crash;
memset( &crash, 0, sizeof(crash) );
crash.type = CrashData::FORCE_CRASH;
GetBacktrace( crash.BacktracePointers[0], BACKTRACE_MAX_SIZE, NULL );
if( iID == GetInvalidThreadId() )
{
/* Backtrace all threads. */
BacktraceAllThreads( crash );
}
else
{
BacktraceContext ctx;
if( !GetThreadBacktraceContext( iID, &ctx ) )
reason += "; GetThreadBacktraceContext failed";
else
GetBacktrace( crash.BacktracePointers[1], BACKTRACE_MAX_SIZE, &ctx );
strncpy( crash.m_ThreadName[1], RageThread::GetThreadNameByID(iID), sizeof(crash.m_ThreadName[0])-1 );
}
strncpy( crash.m_ThreadName[0], RageThread::GetCurrentThreadName(), sizeof(crash.m_ThreadName[0])-1 );
strncpy( crash.reason, reason, min(sizeof(crash.reason) - 1, reason.length()) );
crash.reason[ sizeof(crash.reason)-1 ] = 0;
RunCrashHandler( &crash );
_exit( 1 );
}
void CrashHandler::CrashSignalHandler( int signal, siginfo_t *si, const ucontext_t *uc )
{
static bool bInCrashSignalHandler = false;
if( bInCrashSignalHandler )
{
fprintf(stderr, "Fatal: crash from within the crash signal handler\n");
_exit(1);
}
bInCrashSignalHandler = true;
CrashData crash;
memset( &crash, 0, sizeof(crash) );
crash.type = CrashData::SIGNAL;
crash.signal = signal;
crash.si = *si;
BacktraceContext ctx;
GetSignalBacktraceContext( &ctx, uc );
GetBacktrace( crash.BacktracePointers[0], BACKTRACE_MAX_SIZE, &ctx );
#if defined(HAVE_DECL_SIGUSR1) && HAVE_DECL_SIGUSR1
if( signal == SIGUSR1 )
BacktraceAllThreads( crash );
#endif
strncpy( crash.m_ThreadName[0], RageThread::GetCurrentThreadName(), sizeof(crash.m_ThreadName[0])-1 );
bInCrashSignalHandler = false;
/* RunCrashHandler handles any recursive crashes of its own by itself. */
RunCrashHandler( &crash );
}
void CrashHandler::InitializeCrashHandler()
{
InitializeBacktrace();
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+41 -41
View File
@@ -1,41 +1,41 @@
#ifndef CRASH_HANDLER_H
#define CRASH_HANDLER_H
#include <csignal>
#include <ucontext.h>
namespace CrashHandler
{
void CrashHandlerHandleArgs( int argc, char* argv[] );
void InitializeCrashHandler();
void CrashSignalHandler( int signal, siginfo_t *si, const ucontext_t *uc );
void ForceCrash( const char *reason );
void ForceDeadlock( RString reason, uint64_t CrashHandle );
}
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef CRASH_HANDLER_H
#define CRASH_HANDLER_H
#include <csignal>
#include <ucontext.h>
namespace CrashHandler
{
void CrashHandlerHandleArgs( int argc, char* argv[] );
void InitializeCrashHandler();
void CrashSignalHandler( int signal, siginfo_t *si, const ucontext_t *uc );
void ForceCrash( const char *reason );
void ForceDeadlock( RString reason, uint64_t CrashHandle );
}
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+327 -326
View File
@@ -1,326 +1,327 @@
#define __USE_GNU
#include "global.h"
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/select.h>
#include "Backtrace.h"
#include "BacktraceNames.h"
#include "RageUtil.h"
#include "CrashHandler.h"
#include "CrashHandlerInternal.h"
#include "RageLog.h" /* for RageLog::GetAdditionalLog, etc. only */
#include "ProductInfo.h"
#include "arch/ArchHooks/ArchHooks.h"
#if defined(MACOSX)
#include "archutils/Darwin/Crash.h"
#endif
#if defined(HAVE_VERSION_INFO)
extern const unsigned long version_num;
extern const char *const version_time;
#endif
const char *g_pCrashHandlerArgv0 = NULL;
static void output_stack_trace( FILE *out, const void **BacktracePointers )
{
if( BacktracePointers[0] == BACKTRACE_METHOD_NOT_AVAILABLE )
{
fprintf( out, "No backtrace method available.\n");
return;
}
if( !BacktracePointers[0] )
{
fprintf( out, "Backtrace was empty.\n");
return;
}
for( int i = 0; BacktracePointers[i]; ++i)
{
BacktraceNames bn;
bn.FromAddr( BacktracePointers[i] );
bn.Demangle();
/* Don't show the main module name. */
if( bn.File == g_pCrashHandlerArgv0 && !bn.Symbol.empty() )
bn.File = "";
if( bn.Symbol == "__libc_start_main" )
break;
fprintf( out, "%s\n", bn.Format().c_str() );
}
}
bool child_read( int fd, void *p, int size )
{
char *buf = (char *) p;
int got = 0;
while( got < size )
{
int ret = read( fd, buf+got, size-got );
if( ret == -1 )
{
if( errno == EINTR )
continue;
fprintf( stderr, "Crash handler: error communicating with parent: %s\n", strerror(errno) );
return false;
}
if( ret == 0 )
{
fprintf( stderr, "Crash handler: EOF communicating with parent.\n" );
return false;
}
got += ret;
}
return true;
}
/* Once we get here, we should be * safe to do whatever we want;
* heavyweights like malloc and RString are OK. (Don't crash!) */
static void child_process()
{
/* 1. Read the CrashData. */
CrashData crash;
if( !child_read(3, &crash, sizeof(CrashData)) )
return;
/* 2. Read info. */
int size;
if( !child_read(3, &size, sizeof(size)) )
return;
char *Info = new char [size];
if( !child_read(3, Info, size) )
return;
/* 3. Read AdditionalLog. */
if( !child_read(3, &size, sizeof(size)) )
return;
char *AdditionalLog = new char [size];
if( !child_read(3, AdditionalLog, size) )
return;
/* 4. Read RecentLogs. */
int cnt = 0;
if( !child_read(3, &cnt, sizeof(cnt)) )
return;
char *Recent[1024];
for( int i = 0; i < cnt; ++i )
{
if( !child_read(3, &size, sizeof(size)) )
return;
Recent[i] = new char [size];
if( !child_read(3, Recent[i], size) )
return;
}
/* 5. Read CHECKPOINTs. */
if( !child_read(3, &size, sizeof(size)) )
return;
char *temp = new char [size];
if( !child_read(3, temp, size) )
return;
vector<RString> Checkpoints;
split(temp, "$$", Checkpoints);
delete [] temp;
/* 6. Read the crashed thread's name. */
if( !child_read(3, &size, sizeof(size)) )
return;
temp = new char [size];
if( !child_read(3, temp, size) )
return;
const RString CrashedThread(temp);
delete[] temp;
/* Wait for the child to either finish cleaning up or die. */
fd_set rs;
struct timeval timeout = { 5, 0 }; // 5 seconds
FD_ZERO( &rs );
FD_SET( 3, &rs );
int ret = select( 4, &rs, NULL, NULL, &timeout );
if( ret == 0 )
{
fputs( "Timeout exceeded.\n", stderr );
}
else if( (ret == -1 && errno != EPIPE) || ret != 1 )
{
fprintf( stderr, "Unexpected return from select() result: %d (%s)\n", ret, strerror(errno) );
// Keep going.
}
else
{
char x;
// No need to check FD_ISSET( 3, &rs ) because it was the only descriptor in the set.
ret = read( 3, &x, sizeof(x) );
if( ret > 0 )
{
fprintf( stderr, "Unexpected child read() result: %i\n", ret );
/* keep going */
}
else if( (ret == -1 && errno != EPIPE) || ret != 0 )
{
/* We expect an EOF or EPIPE. What happened? */
fprintf( stderr, "Unexpected child read() result: %i (%s)\n", ret, strerror(errno) );
/* keep going */
}
}
RString sCrashInfoPath = "/tmp";
#if defined(MACOSX)
sCrashInfoPath = CrashHandler::GetLogsDirectory();
#else
const char *home = getenv( "HOME" );
if( home )
sCrashInfoPath = home;
#endif
sCrashInfoPath += "/crashinfo.txt";
FILE *CrashDump = fopen( sCrashInfoPath, "w+" );
if(CrashDump == NULL)
{
fprintf( stderr, "Couldn't open " + sCrashInfoPath + ": %s\n", strerror(errno) );
exit(1);
}
fprintf( CrashDump, "%s crash report", PRODUCT_ID_VER );
#if defined(HAVE_VERSION_INFO)
fprintf( CrashDump, " (build %lu, %s)", version_num, version_time );
#endif
fprintf( CrashDump, "\n" );
fprintf( CrashDump, "--------------------------------------\n" );
fprintf( CrashDump, "\n" );
RString reason;
switch( crash.type )
{
case CrashData::SIGNAL:
{
reason = ssprintf( "%s - %s", SignalName(crash.signal), SignalCodeName(crash.signal, crash.si.si_code) );
/* Linux puts the PID that sent the signal in si_addr for SI_USER. */
if( crash.si.si_code == SI_USER )
{
reason += ssprintf( " from pid %li", (long) crash.si.si_addr );
}
else
{
switch( crash.signal )
{
case SIGILL:
case SIGFPE:
case SIGSEGV:
case SIGBUS:
reason += ssprintf( " at 0x%0*lx", int(sizeof(void*)*2), (unsigned long) crash.si.si_addr );
}
break;
}
}
case CrashData::FORCE_CRASH:
crash.reason[sizeof(crash.reason)-1] = 0;
reason = crash.reason;
break;
}
fprintf( CrashDump, "Architecture: %s\n", HOOKS->GetArchName().c_str() );
fprintf( CrashDump, "Crash reason: %s\n", reason.c_str() );
fprintf( CrashDump, "Crashed thread: %s\n\n", CrashedThread.c_str() );
fprintf(CrashDump, "Checkpoints:\n");
for( unsigned i=0; i<Checkpoints.size(); ++i )
fputs( Checkpoints[i], CrashDump );
fprintf( CrashDump, "\n" );
for( int i = 0; i < CrashData::MAX_BACKTRACE_THREADS; ++i )
{
if( !crash.BacktracePointers[i][0] )
break;
fprintf( CrashDump, "Thread: %s\n", crash.m_ThreadName[i] );
output_stack_trace( CrashDump, crash.BacktracePointers[i] );
fprintf( CrashDump, "\n" );
}
fprintf( CrashDump, "Static log:\n" );
fprintf( CrashDump, "%s", Info );
fprintf( CrashDump, "%s", AdditionalLog );
fprintf(CrashDump, "\nPartial log:\n" );
for( int i = 0; i < cnt; ++i )
fprintf( CrashDump, "%s\n", Recent[i] );
fprintf( CrashDump, "\n" );
fprintf( CrashDump, "-- End of report\n" );
fclose( CrashDump) ;
#if defined(MACOSX)
CrashHandler::InformUserOfCrash( sCrashInfoPath );
#else
/* stdout may have been inadvertently closed by the crash in the parent;
* write to /dev/tty instead. */
FILE *tty = fopen( "/dev/tty", "w" );
if( tty == NULL )
tty = stderr;
fputs( "\n"
PRODUCT_ID " has crashed. Debug information has been output to\n"
"\n"
" " + sCrashInfoPath + "\n"
"\n"
"Please report a bug at:\n"
"\n"
" " REPORT_BUG_URL "\n"
"\n", tty );
#endif
}
void CrashHandler::CrashHandlerHandleArgs( int argc, char* argv[] )
{
g_pCrashHandlerArgv0 = argv[0];
if( argc == 2 && !strcmp(argv[1], CHILD_MAGIC_PARAMETER) )
{
child_process();
exit(0);
}
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#define __USE_GNU
#include "global.h"
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/select.h>
#include "Backtrace.h"
#include "BacktraceNames.h"
#include "RageUtil.h"
#include "CrashHandler.h"
#include "CrashHandlerInternal.h"
#include "RageLog.h" /* for RageLog::GetAdditionalLog, etc. only */
#include "ProductInfo.h"
#include "arch/ArchHooks/ArchHooks.h"
#if defined(MACOSX)
#include "archutils/Darwin/Crash.h"
#endif
#if defined(HAVE_VERSION_INFO)
extern const unsigned long version_num;
extern const char *const version_date;
extern const char *const version_time;
#endif
const char *g_pCrashHandlerArgv0 = NULL;
static void output_stack_trace( FILE *out, const void **BacktracePointers )
{
if( BacktracePointers[0] == BACKTRACE_METHOD_NOT_AVAILABLE )
{
fprintf( out, "No backtrace method available.\n");
return;
}
if( !BacktracePointers[0] )
{
fprintf( out, "Backtrace was empty.\n");
return;
}
for( int i = 0; BacktracePointers[i]; ++i)
{
BacktraceNames bn;
bn.FromAddr( BacktracePointers[i] );
bn.Demangle();
/* Don't show the main module name. */
if( bn.File == g_pCrashHandlerArgv0 && !bn.Symbol.empty() )
bn.File = "";
if( bn.Symbol == "__libc_start_main" )
break;
fprintf( out, "%s\n", bn.Format().c_str() );
}
}
bool child_read( int fd, void *p, int size )
{
char *buf = (char *) p;
int got = 0;
while( got < size )
{
int ret = read( fd, buf+got, size-got );
if( ret == -1 )
{
if( errno == EINTR )
continue;
fprintf( stderr, "Crash handler: error communicating with parent: %s\n", strerror(errno) );
return false;
}
if( ret == 0 )
{
fprintf( stderr, "Crash handler: EOF communicating with parent.\n" );
return false;
}
got += ret;
}
return true;
}
/* Once we get here, we should be * safe to do whatever we want;
* heavyweights like malloc and RString are OK. (Don't crash!) */
static void child_process()
{
/* 1. Read the CrashData. */
CrashData crash;
if( !child_read(3, &crash, sizeof(CrashData)) )
return;
/* 2. Read info. */
int size;
if( !child_read(3, &size, sizeof(size)) )
return;
char *Info = new char [size];
if( !child_read(3, Info, size) )
return;
/* 3. Read AdditionalLog. */
if( !child_read(3, &size, sizeof(size)) )
return;
char *AdditionalLog = new char [size];
if( !child_read(3, AdditionalLog, size) )
return;
/* 4. Read RecentLogs. */
int cnt = 0;
if( !child_read(3, &cnt, sizeof(cnt)) )
return;
char *Recent[1024];
for( int i = 0; i < cnt; ++i )
{
if( !child_read(3, &size, sizeof(size)) )
return;
Recent[i] = new char [size];
if( !child_read(3, Recent[i], size) )
return;
}
/* 5. Read CHECKPOINTs. */
if( !child_read(3, &size, sizeof(size)) )
return;
char *temp = new char [size];
if( !child_read(3, temp, size) )
return;
vector<RString> Checkpoints;
split(temp, "$$", Checkpoints);
delete [] temp;
/* 6. Read the crashed thread's name. */
if( !child_read(3, &size, sizeof(size)) )
return;
temp = new char [size];
if( !child_read(3, temp, size) )
return;
const RString CrashedThread(temp);
delete[] temp;
/* Wait for the child to either finish cleaning up or die. */
fd_set rs;
struct timeval timeout = { 5, 0 }; // 5 seconds
FD_ZERO( &rs );
FD_SET( 3, &rs );
int ret = select( 4, &rs, NULL, NULL, &timeout );
if( ret == 0 )
{
fputs( "Timeout exceeded.\n", stderr );
}
else if( (ret == -1 && errno != EPIPE) || ret != 1 )
{
fprintf( stderr, "Unexpected return from select() result: %d (%s)\n", ret, strerror(errno) );
// Keep going.
}
else
{
char x;
// No need to check FD_ISSET( 3, &rs ) because it was the only descriptor in the set.
ret = read( 3, &x, sizeof(x) );
if( ret > 0 )
{
fprintf( stderr, "Unexpected child read() result: %i\n", ret );
/* keep going */
}
else if( (ret == -1 && errno != EPIPE) || ret != 0 )
{
/* We expect an EOF or EPIPE. What happened? */
fprintf( stderr, "Unexpected child read() result: %i (%s)\n", ret, strerror(errno) );
/* keep going */
}
}
RString sCrashInfoPath = "/tmp";
#if defined(MACOSX)
sCrashInfoPath = CrashHandler::GetLogsDirectory();
#else
const char *home = getenv( "HOME" );
if( home )
sCrashInfoPath = home;
#endif
sCrashInfoPath += "/crashinfo.txt";
FILE *CrashDump = fopen( sCrashInfoPath, "w+" );
if(CrashDump == NULL)
{
fprintf( stderr, "Couldn't open " + sCrashInfoPath + ": %s\n", strerror(errno) );
exit(1);
}
fprintf( CrashDump, "%s crash report", PRODUCT_ID_VER );
#if defined(HAVE_VERSION_INFO)
fprintf( CrashDump, " (build %lu, %s @ %s)", version_num, version_date, version_time );
#endif
fprintf( CrashDump, "\n" );
fprintf( CrashDump, "--------------------------------------\n" );
fprintf( CrashDump, "\n" );
RString reason;
switch( crash.type )
{
case CrashData::SIGNAL:
{
reason = ssprintf( "%s - %s", SignalName(crash.signal), SignalCodeName(crash.signal, crash.si.si_code) );
/* Linux puts the PID that sent the signal in si_addr for SI_USER. */
if( crash.si.si_code == SI_USER )
{
reason += ssprintf( " from pid %li", (long) crash.si.si_addr );
}
else
{
switch( crash.signal )
{
case SIGILL:
case SIGFPE:
case SIGSEGV:
case SIGBUS:
reason += ssprintf( " at 0x%0*lx", int(sizeof(void*)*2), (unsigned long) crash.si.si_addr );
}
break;
}
}
case CrashData::FORCE_CRASH:
crash.reason[sizeof(crash.reason)-1] = 0;
reason = crash.reason;
break;
}
fprintf( CrashDump, "Architecture: %s\n", HOOKS->GetArchName().c_str() );
fprintf( CrashDump, "Crash reason: %s\n", reason.c_str() );
fprintf( CrashDump, "Crashed thread: %s\n\n", CrashedThread.c_str() );
fprintf(CrashDump, "Checkpoints:\n");
for( unsigned i=0; i<Checkpoints.size(); ++i )
fputs( Checkpoints[i], CrashDump );
fprintf( CrashDump, "\n" );
for( int i = 0; i < CrashData::MAX_BACKTRACE_THREADS; ++i )
{
if( !crash.BacktracePointers[i][0] )
break;
fprintf( CrashDump, "Thread: %s\n", crash.m_ThreadName[i] );
output_stack_trace( CrashDump, crash.BacktracePointers[i] );
fprintf( CrashDump, "\n" );
}
fprintf( CrashDump, "Static log:\n" );
fprintf( CrashDump, "%s", Info );
fprintf( CrashDump, "%s", AdditionalLog );
fprintf(CrashDump, "\nPartial log:\n" );
for( int i = 0; i < cnt; ++i )
fprintf( CrashDump, "%s\n", Recent[i] );
fprintf( CrashDump, "\n" );
fprintf( CrashDump, "-- End of report\n" );
fclose( CrashDump) ;
#if defined(MACOSX)
CrashHandler::InformUserOfCrash( sCrashInfoPath );
#else
/* stdout may have been inadvertently closed by the crash in the parent;
* write to /dev/tty instead. */
FILE *tty = fopen( "/dev/tty", "w" );
if( tty == NULL )
tty = stderr;
fputs( "\n"
PRODUCT_ID " has crashed. Debug information has been output to\n"
"\n"
" " + sCrashInfoPath + "\n"
"\n"
"Please report a bug at:\n"
"\n"
" " REPORT_BUG_URL "\n"
"\n", tty );
#endif
}
void CrashHandler::CrashHandlerHandleArgs( int argc, char* argv[] )
{
g_pCrashHandlerArgv0 = argv[0];
if( argc == 2 && !strcmp(argv[1], CHILD_MAGIC_PARAMETER) )
{
child_process();
exit(0);
}
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+211 -211
View File
@@ -1,211 +1,211 @@
#include "global.h"
#include "CrashHandlerInternal.h"
#include <csignal>
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;
}
const char *SignalName( int signo )
{
switch( signo )
{
case SIGALRM: return "Alarm";
case SIGBUS: return "Bus error";
case SIGFPE: return "Floating point exception";
case SIGHUP: return "Hangup";
case SIGILL: return "Illegal instruction";
case SIGINT: return "Interrupt";
case SIGPIPE: return "Broken pipe";
case SIGABRT: return "Aborted";
case SIGQUIT: return "Quit";
case SIGSEGV: return "Segmentation fault";
case SIGTRAP: return "Trace trap";
case SIGTERM: return "Termination";
case SIGVTALRM: return "Virtual alarm clock";
case SIGXCPU: return "CPU limit exceeded";
case SIGXFSZ: return "File size limit exceeded";
#if defined(HAVE_DECL_SIGPWR) && HAVE_DECL_SIGPWR
case SIGPWR: return "Power failure restart";
#endif
#if defined(HAVE_DECL_SIGUSR1) && HAVE_DECL_SIGUSR1
case SIGUSR1: return "Forced crash";
#endif
}
static char buf[128];
strcpy( buf, "Unknown signal " );
strcat( buf, itoa(signo) );
return buf;
}
const char *SignalCodeName( int signo, int code )
{
switch( code )
{
case SI_USER: return "user signal";
case SI_QUEUE: return "sigqueue signal";
case SI_TIMER: return "timer expired";
case SI_MESGQ: return "mesgq state changed";
case SI_ASYNCIO: return "async I/O completed";
#if defined(SI_SIGIO)
case SI_SIGIO: return "queued SIGIO";
#endif
#if defined(SI_KERNEL)
case SI_KERNEL: return "kernel signal";
#endif
}
switch( signo )
{
case SIGILL:
switch( code )
{
case ILL_ILLOPC: return "illegal opcode";
case ILL_ILLTRP: return "illegal trap";
case ILL_PRVOPC: return "privileged opcode";
#if defined(ILL_ILLOPN)
case ILL_ILLOPN: return "illegal operand";
#endif
#if defined(ILL_ILLADR)
case ILL_ILLADR: return "illegal addressing mode";
#endif
#if defined(ILL_PRVREG)
case ILL_PRVREG: return "privileged register";
#endif
#if defined(ILL_COPROC)
case ILL_COPROC: return "coprocessor error";
#endif
#if defined(ILL_BADSTK)
case ILL_BADSTK: return "internal stack error";
#endif
}
break;
case SIGFPE:
switch( code )
{
case FPE_FLTDIV: return "floating point divide by zero";
case FPE_FLTOVF: return "floating point overflow";
case FPE_FLTUND: return "floating point underflow";
case FPE_FLTRES: return "floating point inexact result";
case FPE_FLTINV: return "floating point invalid operation";
#if defined(FPE_INTDIV)
case FPE_INTDIV: return "integer divide by zero";
#endif
#if defined(FPE_INTOVF)
case FPE_INTOVF: return "integer overflow";
#endif
#if defined(FPE_FLTSUB)
case FPE_FLTSUB: return "subscript out of range";
#endif
}
break;
case SIGSEGV:
switch( code )
{
case SEGV_MAPERR: return "address not mapped";
case SEGV_ACCERR: return "invalid permissions";
}
break;
case SIGBUS:
switch( code )
{
case BUS_ADRALN: return "invalid address alignment";
#if defined(BUS_ADRERR)
case BUS_ADRERR: return "nonexistent physical address";
#endif
#if defined(BUS_OBJERR)
case BUS_OBJERR: return "object specific hardware error";
#endif
}
break;
case SIGTRAP:
switch( code )
{
#if defined(TRAP_BRKPT)
case TRAP_BRKPT: return "process breakpoint";
#endif
#if defined(TRAP_TRACE)
case TRAP_TRACE: return "process trace trap";
#endif
}
break;
case SIGCHLD:
switch( code )
{
case CLD_EXITED: return "child has exited";
case CLD_KILLED: return "child was killed";
case CLD_DUMPED: return "child terminated abnormally";
case CLD_TRAPPED: return "traced child has trapped";
case CLD_STOPPED: return "child has stopped";
case CLD_CONTINUED: return "stopped child has continued";
}
break;
#if defined(SIGPOLL)
case SIGPOLL:
switch( code )
{
case POLL_IN: return "data input available";
case POLL_OUT: return "output buffers available";
case POLL_MSG: return "input message available";
case POLL_ERR: return "i/o error";
case POLL_PRI: return "high priority input available";
case POLL_HUP: return "device disconnected";
}
break;
#endif
#if defined(HAVE_DECL_SIGUSR1) && HAVE_DECL_SIGUSR1
case SIGUSR1: return "";
#endif
}
static char buf[128];
strcpy( buf, "Unknown code " );
strcat( buf, itoa(code) );
return buf;
}
/*
* (c) 2003-2006 Glenn Maynard, Steve Checkoway
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "global.h"
#include "CrashHandlerInternal.h"
#include <csignal>
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;
}
const char *SignalName( int signo )
{
switch( signo )
{
case SIGALRM: return "Alarm";
case SIGBUS: return "Bus error";
case SIGFPE: return "Floating point exception";
case SIGHUP: return "Hangup";
case SIGILL: return "Illegal instruction";
case SIGINT: return "Interrupt";
case SIGPIPE: return "Broken pipe";
case SIGABRT: return "Aborted";
case SIGQUIT: return "Quit";
case SIGSEGV: return "Segmentation fault";
case SIGTRAP: return "Trace trap";
case SIGTERM: return "Termination";
case SIGVTALRM: return "Virtual alarm clock";
case SIGXCPU: return "CPU limit exceeded";
case SIGXFSZ: return "File size limit exceeded";
#if defined(HAVE_DECL_SIGPWR) && HAVE_DECL_SIGPWR
case SIGPWR: return "Power failure restart";
#endif
#if defined(HAVE_DECL_SIGUSR1) && HAVE_DECL_SIGUSR1
case SIGUSR1: return "Forced crash";
#endif
}
static char buf[128];
strcpy( buf, "Unknown signal " );
strcat( buf, itoa(signo) );
return buf;
}
const char *SignalCodeName( int signo, int code )
{
switch( code )
{
case SI_USER: return "user signal";
case SI_QUEUE: return "sigqueue signal";
case SI_TIMER: return "timer expired";
case SI_MESGQ: return "mesgq state changed";
case SI_ASYNCIO: return "async I/O completed";
#if defined(SI_SIGIO)
case SI_SIGIO: return "queued SIGIO";
#endif
#if defined(SI_KERNEL)
case SI_KERNEL: return "kernel signal";
#endif
}
switch( signo )
{
case SIGILL:
switch( code )
{
case ILL_ILLOPC: return "illegal opcode";
case ILL_ILLTRP: return "illegal trap";
case ILL_PRVOPC: return "privileged opcode";
#if defined(ILL_ILLOPN)
case ILL_ILLOPN: return "illegal operand";
#endif
#if defined(ILL_ILLADR)
case ILL_ILLADR: return "illegal addressing mode";
#endif
#if defined(ILL_PRVREG)
case ILL_PRVREG: return "privileged register";
#endif
#if defined(ILL_COPROC)
case ILL_COPROC: return "coprocessor error";
#endif
#if defined(ILL_BADSTK)
case ILL_BADSTK: return "internal stack error";
#endif
}
break;
case SIGFPE:
switch( code )
{
case FPE_FLTDIV: return "floating point divide by zero";
case FPE_FLTOVF: return "floating point overflow";
case FPE_FLTUND: return "floating point underflow";
case FPE_FLTRES: return "floating point inexact result";
case FPE_FLTINV: return "floating point invalid operation";
#if defined(FPE_INTDIV)
case FPE_INTDIV: return "integer divide by zero";
#endif
#if defined(FPE_INTOVF)
case FPE_INTOVF: return "integer overflow";
#endif
#if defined(FPE_FLTSUB)
case FPE_FLTSUB: return "subscript out of range";
#endif
}
break;
case SIGSEGV:
switch( code )
{
case SEGV_MAPERR: return "address not mapped";
case SEGV_ACCERR: return "invalid permissions";
}
break;
case SIGBUS:
switch( code )
{
case BUS_ADRALN: return "invalid address alignment";
#if defined(BUS_ADRERR)
case BUS_ADRERR: return "nonexistent physical address";
#endif
#if defined(BUS_OBJERR)
case BUS_OBJERR: return "object specific hardware error";
#endif
}
break;
case SIGTRAP:
switch( code )
{
#if defined(TRAP_BRKPT)
case TRAP_BRKPT: return "process breakpoint";
#endif
#if defined(TRAP_TRACE)
case TRAP_TRACE: return "process trace trap";
#endif
}
break;
case SIGCHLD:
switch( code )
{
case CLD_EXITED: return "child has exited";
case CLD_KILLED: return "child was killed";
case CLD_DUMPED: return "child terminated abnormally";
case CLD_TRAPPED: return "traced child has trapped";
case CLD_STOPPED: return "child has stopped";
case CLD_CONTINUED: return "stopped child has continued";
}
break;
#if defined(SIGPOLL)
case SIGPOLL:
switch( code )
{
case POLL_IN: return "data input available";
case POLL_OUT: return "output buffers available";
case POLL_MSG: return "input message available";
case POLL_ERR: return "i/o error";
case POLL_PRI: return "high priority input available";
case POLL_HUP: return "device disconnected";
}
break;
#endif
#if defined(HAVE_DECL_SIGUSR1) && HAVE_DECL_SIGUSR1
case SIGUSR1: return "";
#endif
}
static char buf[128];
strcpy( buf, "Unknown code " );
strcat( buf, itoa(code) );
return buf;
}
/*
* (c) 2003-2006 Glenn Maynard, Steve Checkoway
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+64 -64
View File
@@ -1,64 +1,64 @@
#ifndef CRASH_HANDLER_INTERNAL_H
#define CRASH_HANDLER_INTERNAL_H
#include "Backtrace.h"
#define BACKTRACE_MAX_SIZE 128
struct CrashData
{
enum CrashType
{
/* We received a fatal signal. si and uc are valid. */
SIGNAL,
/* We're forcing a crash (eg. failed ASSERT). */
FORCE_CRASH,
} type;
/* Everything except FORCE_CRASH_THIS_THREAD: */
enum { MAX_BACKTRACE_THREADS = 32 };
const void *BacktracePointers[MAX_BACKTRACE_THREADS][BACKTRACE_MAX_SIZE];
char m_ThreadName[MAX_BACKTRACE_THREADS][128];
/* SIGNAL only: */
int signal;
siginfo_t si;
/* FORCE_CRASH_THIS_THREAD and FORCE_CRASH_DEADLOCK only: */
char reason[256];
};
#define CHILD_MAGIC_PARAMETER "--private-do-crash-handler"
/* These can return a pointer to static memory. Copy the returned string if you wish to save it. */
const char *itoa( unsigned n );
const char *SignalName( int signo );
const char *SignalCodeName( int signo, int code );
#endif
/*
* (c) 2003-2006 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef CRASH_HANDLER_INTERNAL_H
#define CRASH_HANDLER_INTERNAL_H
#include "Backtrace.h"
#define BACKTRACE_MAX_SIZE 128
struct CrashData
{
enum CrashType
{
/* We received a fatal signal. si and uc are valid. */
SIGNAL,
/* We're forcing a crash (eg. failed ASSERT). */
FORCE_CRASH,
} type;
/* Everything except FORCE_CRASH_THIS_THREAD: */
enum { MAX_BACKTRACE_THREADS = 32 };
const void *BacktracePointers[MAX_BACKTRACE_THREADS][BACKTRACE_MAX_SIZE];
char m_ThreadName[MAX_BACKTRACE_THREADS][128];
/* SIGNAL only: */
int signal;
siginfo_t si;
/* FORCE_CRASH_THIS_THREAD and FORCE_CRASH_DEADLOCK only: */
char reason[256];
};
#define CHILD_MAGIC_PARAMETER "--private-do-crash-handler"
/* These can return a pointer to static memory. Copy the returned string if you wish to save it. */
const char *itoa( unsigned n );
const char *SignalName( int signo );
const char *SignalCodeName( int signo, int code );
#endif
/*
* (c) 2003-2006 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+20 -20
View File
@@ -1,20 +1,20 @@
#include "global.h"
#include "EmergencyShutdown.h"
#include "RageUtil.h"
typedef void (*Callback)();
static Callback g_pEmergencyFunc[5];
static unsigned g_iNumEmergencyFuncs = 0;
void RegisterEmergencyShutdownCallback( void (*pFunc)() )
{
ASSERT( g_iNumEmergencyFuncs+1 < ARRAYLEN(g_pEmergencyFunc) );
g_pEmergencyFunc[ g_iNumEmergencyFuncs++ ] = pFunc;
}
void DoEmergencyShutdown()
{
for( unsigned i = 0; i < g_iNumEmergencyFuncs; ++i )
g_pEmergencyFunc[i]();
}
#include "global.h"
#include "EmergencyShutdown.h"
#include "RageUtil.h"
typedef void (*Callback)();
static Callback g_pEmergencyFunc[5];
static unsigned g_iNumEmergencyFuncs = 0;
void RegisterEmergencyShutdownCallback( void (*pFunc)() )
{
ASSERT( g_iNumEmergencyFuncs+1 < ARRAYLEN(g_pEmergencyFunc) );
g_pEmergencyFunc[ g_iNumEmergencyFuncs++ ] = pFunc;
}
void DoEmergencyShutdown()
{
for( unsigned i = 0; i < g_iNumEmergencyFuncs; ++i )
g_pEmergencyFunc[i]();
}
+8 -8
View File
@@ -1,8 +1,8 @@
#ifndef EMERGENCY_SHUTDOWN_H
#define EMERGENCY_SHUTDOWN_H
void RegisterEmergencyShutdownCallback( void (*pFunc)() );
void DoEmergencyShutdown();
#endif
#ifndef EMERGENCY_SHUTDOWN_H
#define EMERGENCY_SHUTDOWN_H
void RegisterEmergencyShutdownCallback( void (*pFunc)() );
void DoEmergencyShutdown();
#endif
+42 -42
View File
@@ -1,42 +1,42 @@
#include "global.h"
#include "GetSysInfo.h"
#include <sys/utsname.h>
void GetKernel( RString &sys, int &iVersion )
{
struct utsname uts;
uname( &uts );
sys = uts.sysname;
iVersion = 0;
int iMajor = 0, iMinor = 0, iRevision = 0;
if( sscanf( uts.release, "%d.%d.%d", &iMajor, &iMinor, &iRevision ) >= 2 )
iVersion = (iMajor * 10000) + (iMinor * 100) + (iRevision);
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "global.h"
#include "GetSysInfo.h"
#include <sys/utsname.h>
void GetKernel( RString &sys, int &iVersion )
{
struct utsname uts;
uname( &uts );
sys = uts.sysname;
iVersion = 0;
int iMajor = 0, iMinor = 0, iRevision = 0;
if( sscanf( uts.release, "%d.%d.%d", &iMajor, &iMinor, &iRevision ) >= 2 )
iVersion = (iMajor * 10000) + (iMinor * 100) + (iRevision);
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+30 -30
View File
@@ -1,30 +1,30 @@
#ifndef GET_SYS_INFO_H
void GetKernel( RString &sys, int &vers );
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef GET_SYS_INFO_H
void GetKernel( RString &sys, int &vers );
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+297 -297
View File
@@ -1,297 +1,297 @@
/* RageThreads helpers for threads in Linux, which are based on PIDs and TIDs. */
#include "global.h"
#include "LinuxThreadHelpers.h"
#include "RageUtil.h"
#include "RageThreads.h"
#include "Backtrace.h"
#include "archutils/Unix/RunningUnderValgrind.h"
#if defined(LINUX)
#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 <sys/syscall.h>
#define _LINUX_PTRACE_H // hack to prevent broken linux/ptrace.h from conflicting with sys/ptrace.h
#include <sys/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;
#define gettid() syscall(SYS_gettid)
#ifndef _CS_GNU_LIBPTHREAD_VERSION
#define _CS_GNU_LIBPTHREAD_VERSION 3
#endif
RString ThreadsVersion()
{
char buf[1024] = "(error)";
int ret = confstr( _CS_GNU_LIBPTHREAD_VERSION, buf, sizeof(buf) );
if( ret == -1 )
return "(unknown)";
return buf;
}
/* Crash-conditions-safe: */
bool UsingNPTL()
{
char buf[1024] = "";
int ret = confstr( _CS_GNU_LIBPTHREAD_VERSION, buf, sizeof(buf) );
if( ret == -1 )
return false;
return !strncmp( buf, "NPTL", 4 );
}
/* Crash-conditions-safe: */
void InitializePidThreadHelpers()
{
static 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 );
}
/* Get this thread's ID (this may be a TID or a PID). */
static uint64_t GetCurrentThreadIdInternal()
{
/* If we're under Valgrind, neither the PID nor the TID is associated with the
* thread. Return the pthread ID. This can't be used to kill threads, etc.,
* but that only happens under error conditions anyway. If we don't return a
* usable, unique ID, then mutexes won't work. */
if( RunningUnderValgrind() )
return (int) pthread_self();
InitializePidThreadHelpers(); // for g_bUsingNPTL
/* Don't keep calling gettid() if it's not supported; it'll make valgrind spam us. */
static bool GetTidUnsupported = 0;
if( !GetTidUnsupported )
{
pid_t ret = gettid();
/* If this fails with ENOSYS, we're on a kernel before gettid, or we're
* under valgrind. 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 );
GetTidUnsupported = true;
}
return getpid();
}
uint64_t GetCurrentThreadId()
{
#if defined(HAVE_TLS)
/* This is called each time we lock a mutex, and gettid() is a little slow, so
* cache the result if we support TLS. */
if( RageThread::GetSupportsTLS() )
{
static thread_local uint64_t cached_tid = 0;
static thread_local bool cached = false;
if( !cached )
{
cached_tid = GetCurrentThreadIdInternal();
cached = true;
}
return cached_tid;
}
#endif
return GetCurrentThreadIdInternal();
}
int SuspendThread( uint64_t 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( int(ThreadID) );
// kill( ThreadID, SIGSTOP );
}
int ResumeThread( uint64_t ThreadID )
{
return PtraceDetach( int(ThreadID) );
// kill( ThreadID, SIGSTOP );
}
/* Get a BacktraceContext for a thread. ThreadID 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.
*
* This call leaves the given thread suspended, so the returned context doesn't become invalid.
* ResumeThread() can be used to resume a thread after this call. */
#if defined(CRASH_HANDLER)
bool GetThreadBacktraceContext( uint64_t ThreadID, BacktraceContext *ctx )
{
/* Can't GetThreadBacktraceContext the current thread. */
ASSERT( ThreadID != GetCurrentThreadId() );
/* Attach to the thread. 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. */
if( PtraceAttach( int(ThreadID) ) == -1 && errno != EPERM )
{
CHECKPOINT_M( ssprintf( "%s (pid %i tid %i locking tid %i)",
strerror(errno), getpid(), (int)GetCurrentThreadId(), int(ThreadID) ) );
return false;
}
#if defined(CPU_X86_64) || defined(CPU_X86)
user_regs_struct regs;
if( ptrace( PTRACE_GETREGS, pid_t(ThreadID), NULL, &regs ) == -1 )
return false;
ctx->pid = pid_t(ThreadID);
#if defined(CPU_X86_64)
ctx->ip = (void *) regs.rip;
ctx->bp = (void *) regs.rbp;
ctx->sp = (void *) regs.rsp;
#else
ctx->ip = (void *) regs.eip;
ctx->bp = (void *) regs.ebp;
ctx->sp = (void *) regs.esp;
#endif
#elif defined(CPU_PPC)
errno = 0;
ctx->FramePtr = (const Frame *)ptrace( PTRACE_PEEKUSER, pid_t(ThreadID), (void *)(PT_R1<<2), 0 );
if( errno )
return false;
ctx->PC = (void *)ptrace( PTRACE_PEEKUSER, pid_t(ThreadID), (void *)(PT_NIP<<2), 0 );
if( errno )
return false;
#else
#error GetThreadBacktraceContext: which arch?
#endif
return true;
}
#endif
#elif defined(UNIX)
#include <pthread.h>
#include <signal.h>
RString ThreadsVersion()
{
return "(unknown)";
}
uint64_t GetCurrentThreadId()
{
return uint64_t( pthread_self() );
}
int SuspendThread( uint64_t id )
{
return pthread_kill( pthread_t(id), SIGSTOP );
}
int ResumeThread( uint64_t id )
{
return pthread_kill( pthread_t(id), SIGCONT );
}
#endif
/*
* (c) 2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/* RageThreads helpers for threads in Linux, which are based on PIDs and TIDs. */
#include "global.h"
#include "LinuxThreadHelpers.h"
#include "RageUtil.h"
#include "RageThreads.h"
#include "Backtrace.h"
#include "archutils/Unix/RunningUnderValgrind.h"
#if defined(LINUX)
#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 <sys/syscall.h>
#define _LINUX_PTRACE_H // hack to prevent broken linux/ptrace.h from conflicting with sys/ptrace.h
#include <sys/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;
#define gettid() syscall(SYS_gettid)
#ifndef _CS_GNU_LIBPTHREAD_VERSION
#define _CS_GNU_LIBPTHREAD_VERSION 3
#endif
RString ThreadsVersion()
{
char buf[1024] = "(error)";
int ret = confstr( _CS_GNU_LIBPTHREAD_VERSION, buf, sizeof(buf) );
if( ret == -1 )
return "(unknown)";
return buf;
}
/* Crash-conditions-safe: */
bool UsingNPTL()
{
char buf[1024] = "";
int ret = confstr( _CS_GNU_LIBPTHREAD_VERSION, buf, sizeof(buf) );
if( ret == -1 )
return false;
return !strncmp( buf, "NPTL", 4 );
}
/* Crash-conditions-safe: */
void InitializePidThreadHelpers()
{
static 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 );
}
/* Get this thread's ID (this may be a TID or a PID). */
static uint64_t GetCurrentThreadIdInternal()
{
/* If we're under Valgrind, neither the PID nor the TID is associated with the
* thread. Return the pthread ID. This can't be used to kill threads, etc.,
* but that only happens under error conditions anyway. If we don't return a
* usable, unique ID, then mutexes won't work. */
if( RunningUnderValgrind() )
return (int) pthread_self();
InitializePidThreadHelpers(); // for g_bUsingNPTL
/* Don't keep calling gettid() if it's not supported; it'll make valgrind spam us. */
static bool GetTidUnsupported = 0;
if( !GetTidUnsupported )
{
pid_t ret = gettid();
/* If this fails with ENOSYS, we're on a kernel before gettid, or we're
* under valgrind. 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 );
GetTidUnsupported = true;
}
return getpid();
}
uint64_t GetCurrentThreadId()
{
#if defined(HAVE_TLS)
/* This is called each time we lock a mutex, and gettid() is a little slow, so
* cache the result if we support TLS. */
if( RageThread::GetSupportsTLS() )
{
static thread_local uint64_t cached_tid = 0;
static thread_local bool cached = false;
if( !cached )
{
cached_tid = GetCurrentThreadIdInternal();
cached = true;
}
return cached_tid;
}
#endif
return GetCurrentThreadIdInternal();
}
int SuspendThread( uint64_t 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( int(ThreadID) );
// kill( ThreadID, SIGSTOP );
}
int ResumeThread( uint64_t ThreadID )
{
return PtraceDetach( int(ThreadID) );
// kill( ThreadID, SIGSTOP );
}
/* Get a BacktraceContext for a thread. ThreadID 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.
*
* This call leaves the given thread suspended, so the returned context doesn't become invalid.
* ResumeThread() can be used to resume a thread after this call. */
#if defined(CRASH_HANDLER)
bool GetThreadBacktraceContext( uint64_t ThreadID, BacktraceContext *ctx )
{
/* Can't GetThreadBacktraceContext the current thread. */
ASSERT( ThreadID != GetCurrentThreadId() );
/* Attach to the thread. 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. */
if( PtraceAttach( int(ThreadID) ) == -1 && errno != EPERM )
{
CHECKPOINT_M( ssprintf( "%s (pid %i tid %i locking tid %i)",
strerror(errno), getpid(), (int)GetCurrentThreadId(), int(ThreadID) ) );
return false;
}
#if defined(CPU_X86_64) || defined(CPU_X86)
user_regs_struct regs;
if( ptrace( PTRACE_GETREGS, pid_t(ThreadID), NULL, &regs ) == -1 )
return false;
ctx->pid = pid_t(ThreadID);
#if defined(CPU_X86_64)
ctx->ip = (void *) regs.rip;
ctx->bp = (void *) regs.rbp;
ctx->sp = (void *) regs.rsp;
#else
ctx->ip = (void *) regs.eip;
ctx->bp = (void *) regs.ebp;
ctx->sp = (void *) regs.esp;
#endif
#elif defined(CPU_PPC)
errno = 0;
ctx->FramePtr = (const Frame *)ptrace( PTRACE_PEEKUSER, pid_t(ThreadID), (void *)(PT_R1<<2), 0 );
if( errno )
return false;
ctx->PC = (void *)ptrace( PTRACE_PEEKUSER, pid_t(ThreadID), (void *)(PT_NIP<<2), 0 );
if( errno )
return false;
#else
#error GetThreadBacktraceContext: which arch?
#endif
return true;
}
#endif
#elif defined(UNIX)
#include <pthread.h>
#include <signal.h>
RString ThreadsVersion()
{
return "(unknown)";
}
uint64_t GetCurrentThreadId()
{
return uint64_t( pthread_self() );
}
int SuspendThread( uint64_t id )
{
return pthread_kill( pthread_t(id), SIGSTOP );
}
int ResumeThread( uint64_t id )
{
return pthread_kill( pthread_t(id), SIGCONT );
}
#endif
/*
* (c) 2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+43 -43
View File
@@ -1,43 +1,43 @@
#ifndef PID_THREAD_HELPERS_H
#define PID_THREAD_HELPERS_H
RString ThreadsVersion();
/* Get the current thread's ThreadID. */
uint64_t GetCurrentThreadId();
/* Return true if NPTL libraries are in use, false if linuxthreads. */
bool UsingNPTL();
int SuspendThread( uint64_t ThreadID );
int ResumeThread( uint64_t ThreadID );
struct BacktraceContext;
int GetThreadContext( uint64_t ThreadID, BacktraceContext *ctx );
#endif
/*
* (c) 2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef PID_THREAD_HELPERS_H
#define PID_THREAD_HELPERS_H
RString ThreadsVersion();
/* Get the current thread's ThreadID. */
uint64_t GetCurrentThreadId();
/* Return true if NPTL libraries are in use, false if linuxthreads. */
bool UsingNPTL();
int SuspendThread( uint64_t ThreadID );
int ResumeThread( uint64_t ThreadID );
struct BacktraceContext;
int GetThreadContext( uint64_t ThreadID, BacktraceContext *ctx );
#endif
/*
* (c) 2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+57 -57
View File
@@ -1,57 +1,57 @@
#include "global.h"
#include "RunningUnderValgrind.h"
#if defined(CPU_X86) && defined(__GNUC__)
bool RunningUnderValgrind()
{
/* Valgrind crashes and burns on pthread_mutex_timedlock. */
static int under_valgrind = -1;
if( under_valgrind == -1 )
{
unsigned int magic[8] = { 0x00001001, 0, 0, 0, 0, 0, 0, 0 };
asm(
"mov %1, %%eax\n"
"mov $0, %%edx\n"
"rol $29, %%eax\n"
"rol $3, %%eax\n"
"ror $27, %%eax\n"
"ror $5, %%eax\n"
"rol $13, %%eax\n"
"rol $19, %%eax\n"
"mov %%edx, %0\t"
: "=r" (under_valgrind): "r" (magic): "eax", "edx" );
}
return under_valgrind != 0;
}
#else
bool RunningUnderValgrind()
{
return false;
}
#endif
/*
* (c) 2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "global.h"
#include "RunningUnderValgrind.h"
#if defined(CPU_X86) && defined(__GNUC__)
bool RunningUnderValgrind()
{
/* Valgrind crashes and burns on pthread_mutex_timedlock. */
static int under_valgrind = -1;
if( under_valgrind == -1 )
{
unsigned int magic[8] = { 0x00001001, 0, 0, 0, 0, 0, 0, 0 };
asm(
"mov %1, %%eax\n"
"mov $0, %%edx\n"
"rol $29, %%eax\n"
"rol $3, %%eax\n"
"ror $27, %%eax\n"
"ror $5, %%eax\n"
"rol $13, %%eax\n"
"rol $19, %%eax\n"
"mov %%edx, %0\t"
: "=r" (under_valgrind): "r" (magic): "eax", "edx" );
}
return under_valgrind != 0;
}
#else
bool RunningUnderValgrind()
{
return false;
}
#endif
/*
* (c) 2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+33 -33
View File
@@ -1,33 +1,33 @@
/* RunningUnderValgrind - Detect if the application is being run under Valgrind. */
#ifndef DETECT_VALGRIND_H
#define DETECT_VALGRIND_H
bool RunningUnderValgrind();
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/* RunningUnderValgrind - Detect if the application is being run under Valgrind. */
#ifndef DETECT_VALGRIND_H
#define DETECT_VALGRIND_H
bool RunningUnderValgrind();
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+221 -221
View File
@@ -1,221 +1,221 @@
#include "global.h"
#include "RageLog.h"
#include "SignalHandler.h"
#include "GetSysInfo.h"
#if defined(UNIX)
#include "archutils/Unix/LinuxThreadHelpers.h"
#endif
#include <unistd.h>
#include <sys/mman.h>
#include <cerrno>
#if defined(MACOSX)
extern "C" int sigaltstack(const stack_t * __restrict, stack_t * __restrict);
#endif
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
# define MAP_ANONYMOUS MAP_ANON
#endif
#if defined(__GNUC__)
# define NOINLINE __attribute__((__noinline__))
#else
# define NOINLINE
#endif
static int find_stack_direction2( char *p ) NOINLINE;
static int find_stack_direction() NOINLINE;
static vector<SignalHandler::handler> handlers;
SaveSignals *saved_sigs;
static int signals[] =
{
SIGALRM, SIGBUS, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGABRT,
SIGQUIT, SIGSEGV, SIGTRAP, SIGTERM, SIGVTALRM, SIGXCPU, SIGXFSZ,
#if defined(HAVE_DECL_SIGPWR) && HAVE_DECL_SIGPWR
SIGPWR,
#endif
#if defined(HAVE_DECL_SIGUSR1) && HAVE_DECL_SIGUSR1
SIGUSR1,
#endif
-1
};
/*
* Store signals and restore them. This lets us clean up after libraries
* that like to mess with our signal handler. (Of course, if we undo
* signal handlers for libraries, we need to be sure to handle whatever it
* was cleaning up by hand.)
*/
SaveSignals::SaveSignals()
{
/* Store the old signal handlers. */
for( int i = 0; signals[i] != -1; ++i )
{
struct sigaction sa;
sigaction( signals[i], NULL, &sa );
old_handlers.push_back( sa );
}
}
SaveSignals::~SaveSignals()
{
/* Restore the old signal handlers. */
for( unsigned i = 0; i < old_handlers.size(); ++i )
sigaction( signals[i], &old_handlers[i], NULL );
}
static void SigHandler( int signal, siginfo_t *si, void *ucp )
{
bool bMaskSignal = false;
for( unsigned i = 0; i < handlers.size(); ++i )
bMaskSignal |= handlers[i]( signal, si, (const ucontext_t *)ucp );
if( !bMaskSignal )
{
struct sigaction sa;
sa.sa_flags = 0;
sigemptyset( &sa.sa_mask );
/* Set up the default signal handler. */
sa.sa_handler = SIG_DFL;
struct sigaction old;
sigaction( signal, &sa, &old );
raise( signal );
sigaction( signal, &old, NULL );
}
}
int find_stack_direction2( char *p )
{
char c;
return (&c > p) ? +1:-1;
}
int find_stack_direction()
{
char c;
return find_stack_direction2( &c );
}
/* Create a stack with a barrier page at the end to guard against stack overflow. */
static void *CreateStack( int size )
{
const long PageSize = getpagesize();
/* Round size up to the nearest PageSize. */
size += PageSize-1;
size -= (size%PageSize);
/* mmap always returns page-aligned data.
*
* mmap entries always show up individually in /proc/#/maps. We could use posix_memalign as
* a fallback, but we'd have to put a barrier page on both sides to guarantee that. */
char *p = NULL;
p = (char *) mmap( NULL, size+PageSize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 );
if( p == (void *) -1 )
return NULL;
// if( posix_memalign( (void**) &p, PageSize, RealSize ) != 0 )
// return NULL;
if( find_stack_direction() < 0 )
{
/* The stack grows towards smaller addresses. Protect the first page. */
mprotect( p, PageSize, PROT_NONE );
p += PageSize;
}
else
{
/* The stack grows towards larger addresses. Protect the last page. */
mprotect( p+size, PageSize, PROT_NONE );
}
return p;
}
/* Hook up events to fatal signals, so we can clean up if we're killed. */
void SignalHandler::OnClose( handler h )
{
if( saved_sigs == NULL )
{
saved_sigs = new SaveSignals;
bool bUseAltSigStack = true;
#if defined(LINUX)
/* Linuxthreads (pre-NPTL) sigaltstack is broken. */
if( !UsingNPTL() )
bUseAltSigStack = false;
#endif
/* Allocate a separate signal stack. This makes the crash handler work
* if we run out of stack space. */
const int AltStackSize = 1024*64;
void *p = NULL;
if( bUseAltSigStack )
p = CreateStack( AltStackSize );
if( p != NULL )
{
stack_t ss;
ss.ss_sp = (char*)p; /* cast for Darwin */
ss.ss_size = AltStackSize;
ss.ss_flags = 0;
if( sigaltstack( &ss, NULL ) == -1 )
{
LOG->Info( "sigaltstack failed: %s", strerror(errno) );
p = NULL; /* no SA_ONSTACK */
}
}
struct sigaction sa;
sa.sa_flags = 0;
if( p != NULL )
sa.sa_flags |= SA_ONSTACK;
sa.sa_flags |= SA_NODEFER;
sa.sa_flags |= SA_SIGINFO;
sigemptyset(&sa.sa_mask);
/* Set up our signal handlers. */
sa.sa_sigaction = SigHandler;
for( int i = 0; signals[i] != -1; ++i )
sigaction( signals[i], &sa, NULL );
/* Block SIGPIPE, so we get EPIPE. */
sa.sa_handler = SIG_IGN;
sigaction( SIGPIPE, &sa, NULL );
}
handlers.push_back(h);
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "global.h"
#include "RageLog.h"
#include "SignalHandler.h"
#include "GetSysInfo.h"
#if defined(UNIX)
#include "archutils/Unix/LinuxThreadHelpers.h"
#endif
#include <unistd.h>
#include <sys/mman.h>
#include <cerrno>
#if defined(MACOSX)
extern "C" int sigaltstack(const stack_t * __restrict, stack_t * __restrict);
#endif
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
# define MAP_ANONYMOUS MAP_ANON
#endif
#if defined(__GNUC__)
# define NOINLINE __attribute__((__noinline__))
#else
# define NOINLINE
#endif
static int find_stack_direction2( char *p ) NOINLINE;
static int find_stack_direction() NOINLINE;
static vector<SignalHandler::handler> handlers;
SaveSignals *saved_sigs;
static int signals[] =
{
SIGALRM, SIGBUS, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGABRT,
SIGQUIT, SIGSEGV, SIGTRAP, SIGTERM, SIGVTALRM, SIGXCPU, SIGXFSZ,
#if defined(HAVE_DECL_SIGPWR) && HAVE_DECL_SIGPWR
SIGPWR,
#endif
#if defined(HAVE_DECL_SIGUSR1) && HAVE_DECL_SIGUSR1
SIGUSR1,
#endif
-1
};
/*
* Store signals and restore them. This lets us clean up after libraries
* that like to mess with our signal handler. (Of course, if we undo
* signal handlers for libraries, we need to be sure to handle whatever it
* was cleaning up by hand.)
*/
SaveSignals::SaveSignals()
{
/* Store the old signal handlers. */
for( int i = 0; signals[i] != -1; ++i )
{
struct sigaction sa;
sigaction( signals[i], NULL, &sa );
old_handlers.push_back( sa );
}
}
SaveSignals::~SaveSignals()
{
/* Restore the old signal handlers. */
for( unsigned i = 0; i < old_handlers.size(); ++i )
sigaction( signals[i], &old_handlers[i], NULL );
}
static void SigHandler( int signal, siginfo_t *si, void *ucp )
{
bool bMaskSignal = false;
for( unsigned i = 0; i < handlers.size(); ++i )
bMaskSignal |= handlers[i]( signal, si, (const ucontext_t *)ucp );
if( !bMaskSignal )
{
struct sigaction sa;
sa.sa_flags = 0;
sigemptyset( &sa.sa_mask );
/* Set up the default signal handler. */
sa.sa_handler = SIG_DFL;
struct sigaction old;
sigaction( signal, &sa, &old );
raise( signal );
sigaction( signal, &old, NULL );
}
}
int find_stack_direction2( char *p )
{
char c;
return (&c > p) ? +1:-1;
}
int find_stack_direction()
{
char c;
return find_stack_direction2( &c );
}
/* Create a stack with a barrier page at the end to guard against stack overflow. */
static void *CreateStack( int size )
{
const long PageSize = getpagesize();
/* Round size up to the nearest PageSize. */
size += PageSize-1;
size -= (size%PageSize);
/* mmap always returns page-aligned data.
*
* mmap entries always show up individually in /proc/#/maps. We could use posix_memalign as
* a fallback, but we'd have to put a barrier page on both sides to guarantee that. */
char *p = NULL;
p = (char *) mmap( NULL, size+PageSize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 );
if( p == (void *) -1 )
return NULL;
// if( posix_memalign( (void**) &p, PageSize, RealSize ) != 0 )
// return NULL;
if( find_stack_direction() < 0 )
{
/* The stack grows towards smaller addresses. Protect the first page. */
mprotect( p, PageSize, PROT_NONE );
p += PageSize;
}
else
{
/* The stack grows towards larger addresses. Protect the last page. */
mprotect( p+size, PageSize, PROT_NONE );
}
return p;
}
/* Hook up events to fatal signals, so we can clean up if we're killed. */
void SignalHandler::OnClose( handler h )
{
if( saved_sigs == NULL )
{
saved_sigs = new SaveSignals;
bool bUseAltSigStack = true;
#if defined(LINUX)
/* Linuxthreads (pre-NPTL) sigaltstack is broken. */
if( !UsingNPTL() )
bUseAltSigStack = false;
#endif
/* Allocate a separate signal stack. This makes the crash handler work
* if we run out of stack space. */
const int AltStackSize = 1024*64;
void *p = NULL;
if( bUseAltSigStack )
p = CreateStack( AltStackSize );
if( p != NULL )
{
stack_t ss;
ss.ss_sp = (char*)p; /* cast for Darwin */
ss.ss_size = AltStackSize;
ss.ss_flags = 0;
if( sigaltstack( &ss, NULL ) == -1 )
{
LOG->Info( "sigaltstack failed: %s", strerror(errno) );
p = NULL; /* no SA_ONSTACK */
}
}
struct sigaction sa;
sa.sa_flags = 0;
if( p != NULL )
sa.sa_flags |= SA_ONSTACK;
sa.sa_flags |= SA_NODEFER;
sa.sa_flags |= SA_SIGINFO;
sigemptyset(&sa.sa_mask);
/* Set up our signal handlers. */
sa.sa_sigaction = SigHandler;
for( int i = 0; signals[i] != -1; ++i )
sigaction( signals[i], &sa, NULL );
/* Block SIGPIPE, so we get EPIPE. */
sa.sa_handler = SIG_IGN;
sigaction( SIGPIPE, &sa, NULL );
}
handlers.push_back(h);
}
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+49 -49
View File
@@ -1,49 +1,49 @@
#ifndef SIGNAL_HANDLER_H
#define SIGNAL_HANDLER_H
#include <csignal>
#include <ucontext.h>
class SaveSignals
{
vector<struct sigaction> old_handlers;
public:
SaveSignals(); /* save signals */
~SaveSignals(); /* restore signals */
};
namespace SignalHandler
{
typedef bool (*handler)( int, siginfo_t *si, const ucontext_t *uc );
void OnClose( handler );
};
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef SIGNAL_HANDLER_H
#define SIGNAL_HANDLER_H
#include <csignal>
#include <ucontext.h>
class SaveSignals
{
vector<struct sigaction> old_handlers;
public:
SaveSignals(); /* save signals */
~SaveSignals(); /* restore signals */
};
namespace SignalHandler
{
typedef bool (*handler)( int, siginfo_t *si, const ucontext_t *uc );
void OnClose( handler );
};
#endif
/*
* (c) 2003-2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+33 -33
View File
@@ -1,33 +1,33 @@
#include "global.h"
/* Newer g++ versions (around 4.2) output __stack_chk_fail calls. This is grossly
* backwards-incompatible. It can be disabled, but you'd have to disable it in any
* statically linked libraries: all Ubuntu static libraries have this enabled. Work
* around it by declaring the symbol manually. */
extern "C" void __stack_chk_fail (void) __attribute__ ((__weak__)) __attribute__ ((__noreturn__));
extern "C" void __stack_chk_fail (void) { abort(); }
/*
* (c) 2008 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "global.h"
/* Newer g++ versions (around 4.2) output __stack_chk_fail calls. This is grossly
* backwards-incompatible. It can be disabled, but you'd have to disable it in any
* statically linked libraries: all Ubuntu static libraries have this enabled. Work
* around it by declaring the symbol manually. */
extern "C" void __stack_chk_fail (void) __attribute__ ((__weak__)) __attribute__ ((__noreturn__));
extern "C" void __stack_chk_fail (void) { abort(); }
/*
* (c) 2008 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+117 -117
View File
@@ -1,117 +1,117 @@
#include "global.h"
#include "X11Helper.h"
#include "RageLog.h"
Display *X11Helper::Dpy = NULL;
Window X11Helper::Win = None;
static int ErrorCallback( Display*, XErrorEvent* );
static int FatalCallback( Display* );
bool X11Helper::OpenXConnection()
{
DEBUG_ASSERT( Dpy == NULL && Win == None );
Dpy = XOpenDisplay(0);
if( Dpy == NULL )
return false;
XSetIOErrorHandler( FatalCallback );
XSetErrorHandler( ErrorCallback );
return true;
}
void X11Helper::CloseXConnection()
{
// The window should have been shut down
DEBUG_ASSERT( Dpy != NULL );
DEBUG_ASSERT( Win == None );
XCloseDisplay( Dpy );
Dpy = NULL;
}
bool X11Helper::MakeWindow( Window &win, int screenNum, int depth, Visual *visual, int width, int height, bool overrideRedirect )
{
if( !Dpy )
return false;
XSetWindowAttributes winAttribs;
winAttribs.border_pixel = 0;
winAttribs.event_mask = 0L;
if( win )
{
// Preserve the event mask.
XWindowAttributes attribs;
XGetWindowAttributes( Dpy, win, &attribs );
winAttribs.event_mask = attribs.your_event_mask;
XDestroyWindow( Dpy, win );
}
// XXX: Error catching/handling?
winAttribs.colormap = XCreateColormap( Dpy, RootWindow(Dpy, screenNum), visual, AllocNone );
unsigned long mask = CWBorderPixel | CWColormap | CWEventMask;
if( overrideRedirect )
{
winAttribs.override_redirect = True;
mask |= CWOverrideRedirect;
}
win = XCreateWindow( Dpy, RootWindow(Dpy, screenNum), 0, 0, width, height, 0,
depth, InputOutput, visual, mask, &winAttribs );
if( win == None )
return false;
/* Hide the mouse cursor. */
{
const char pBlank[] = { 0,0,0,0,0,0,0,0 };
Pixmap BlankBitmap = XCreateBitmapFromData( Dpy, win, pBlank, 8, 8 );
XColor black = { 0, 0, 0, 0, 0, 0 };
Cursor pBlankPointer = XCreatePixmapCursor( Dpy, BlankBitmap, BlankBitmap, &black, &black, 0, 0 );
XFreePixmap( Dpy, BlankBitmap );
XDefineCursor( Dpy, win, pBlankPointer );
XFreeCursor( Dpy, pBlankPointer );
}
return true;
}
int ErrorCallback( Display *d, XErrorEvent *err )
{
char errText[512];
XGetErrorText( d, err->error_code, errText, 512 );
LOG->Warn( "X11 Protocol error %s (%d) has occurred, caused by request %d,%d, resource ID %d",
errText, err->error_code, err->request_code, err->minor_code, (int) err->resourceid );
return 0; // Xlib ignores our return value
}
int FatalCallback( Display *d )
{
RageException::Throw( "Fatal I/O error communicating with X server." );
}
/*
* (c) 2005, 2006 Ben Anderson, Steve Checkoway
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "global.h"
#include "X11Helper.h"
#include "RageLog.h"
Display *X11Helper::Dpy = NULL;
Window X11Helper::Win = None;
static int ErrorCallback( Display*, XErrorEvent* );
static int FatalCallback( Display* );
bool X11Helper::OpenXConnection()
{
DEBUG_ASSERT( Dpy == NULL && Win == None );
Dpy = XOpenDisplay(0);
if( Dpy == NULL )
return false;
XSetIOErrorHandler( FatalCallback );
XSetErrorHandler( ErrorCallback );
return true;
}
void X11Helper::CloseXConnection()
{
// The window should have been shut down
DEBUG_ASSERT( Dpy != NULL );
DEBUG_ASSERT( Win == None );
XCloseDisplay( Dpy );
Dpy = NULL;
}
bool X11Helper::MakeWindow( Window &win, int screenNum, int depth, Visual *visual, int width, int height, bool overrideRedirect )
{
if( !Dpy )
return false;
XSetWindowAttributes winAttribs;
winAttribs.border_pixel = 0;
winAttribs.event_mask = 0L;
if( win )
{
// Preserve the event mask.
XWindowAttributes attribs;
XGetWindowAttributes( Dpy, win, &attribs );
winAttribs.event_mask = attribs.your_event_mask;
XDestroyWindow( Dpy, win );
}
// XXX: Error catching/handling?
winAttribs.colormap = XCreateColormap( Dpy, RootWindow(Dpy, screenNum), visual, AllocNone );
unsigned long mask = CWBorderPixel | CWColormap | CWEventMask;
if( overrideRedirect )
{
winAttribs.override_redirect = True;
mask |= CWOverrideRedirect;
}
win = XCreateWindow( Dpy, RootWindow(Dpy, screenNum), 0, 0, width, height, 0,
depth, InputOutput, visual, mask, &winAttribs );
if( win == None )
return false;
/* Hide the mouse cursor. */
{
const char pBlank[] = { 0,0,0,0,0,0,0,0 };
Pixmap BlankBitmap = XCreateBitmapFromData( Dpy, win, pBlank, 8, 8 );
XColor black = { 0, 0, 0, 0, 0, 0 };
Cursor pBlankPointer = XCreatePixmapCursor( Dpy, BlankBitmap, BlankBitmap, &black, &black, 0, 0 );
XFreePixmap( Dpy, BlankBitmap );
XDefineCursor( Dpy, win, pBlankPointer );
XFreeCursor( Dpy, pBlankPointer );
}
return true;
}
int ErrorCallback( Display *d, XErrorEvent *err )
{
char errText[512];
XGetErrorText( d, err->error_code, errText, 512 );
LOG->Warn( "X11 Protocol error %s (%d) has occurred, caused by request %d,%d, resource ID %d",
errText, err->error_code, err->request_code, err->minor_code, (int) err->resourceid );
return 0; // Xlib ignores our return value
}
int FatalCallback( Display *d )
{
RageException::Throw( "Fatal I/O error communicating with X server." );
}
/*
* (c) 2005, 2006 Ben Anderson, Steve Checkoway
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+56 -56
View File
@@ -1,56 +1,56 @@
/* Manages our X connection and window. */
#ifndef X11_HELPER_H
#define X11_HELPER_H
#include <X11/Xlib.h> // Window
namespace X11Helper
{
// All functions in here that return a bool return true on success, and
// false on failure.
// Create the connection, if necessary; otherwise do some important
// internal session-tracking stuff (so you should call this anyway).
bool OpenXConnection();
// Destroy the connection, if appropriate; otherwise do some important
// internal session-tracking stuff (so you should call it anyway).
void CloseXConnection();
// The current Display (connection). Initialized by the first call to
// OpenXConnection().
extern Display *Dpy;
// The Window used by LowLevelWindow_X11 as the main window.
extern Window Win;
// (Re)create the Window win.
bool MakeWindow( Window &win, int screenNum, int depth, Visual *visual,
int width, int height, bool overrideRedirect );
};
#endif
/*
* (c) 2005, 2006 Ben Anderson, Steve Checkoway
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/* Manages our X connection and window. */
#ifndef X11_HELPER_H
#define X11_HELPER_H
#include <X11/Xlib.h> // Window
namespace X11Helper
{
// All functions in here that return a bool return true on success, and
// false on failure.
// Create the connection, if necessary; otherwise do some important
// internal session-tracking stuff (so you should call this anyway).
bool OpenXConnection();
// Destroy the connection, if appropriate; otherwise do some important
// internal session-tracking stuff (so you should call it anyway).
void CloseXConnection();
// The current Display (connection). Initialized by the first call to
// OpenXConnection().
extern Display *Dpy;
// The Window used by LowLevelWindow_X11 as the main window.
extern Window Win;
// (Re)create the Window win.
bool MakeWindow( Window &win, int screenNum, int depth, Visual *visual,
int width, int height, bool overrideRedirect );
};
#endif
/*
* (c) 2005, 2006 Ben Anderson, Steve Checkoway
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+58 -58
View File
@@ -1,58 +1,58 @@
#ifndef ARCH_SETUP_UNIX_H
#define ARCH_SETUP_UNIX_H
#if !defined(_STDC_C99) && !defined(__C99FEATURES__)
#define __C99FEATURES__
#endif
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#if !defined(MISSING_STDINT_H) /* need to define int64_t if so */
#include <stdint.h>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef BSD
#undef ALIGN
#undef MACHINE
#if defined(__NetBSD_Version__) && __NetBSD_Version__ < 299000900
#define lrintf(x) ((int)rint(x))
#endif
#endif
/* For RageLog */
#define HAVE_VERSION_INFO
#include "archutils/Common/gcc_byte_swaps.h"
#define attribute_deprecated // Shut ffmpeg up!
#endif
/*
* (c) 2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef ARCH_SETUP_UNIX_H
#define ARCH_SETUP_UNIX_H
#if !defined(_STDC_C99) && !defined(__C99FEATURES__)
#define __C99FEATURES__
#endif
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#if !defined(MISSING_STDINT_H) /* need to define int64_t if so */
#include <stdint.h>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef BSD
#undef ALIGN
#undef MACHINE
#if defined(__NetBSD_Version__) && __NetBSD_Version__ < 299000900
#define lrintf(x) ((int)rint(x))
#endif
#endif
/* For RageLog */
#define HAVE_VERSION_INFO
#include "archutils/Common/gcc_byte_swaps.h"
#define attribute_deprecated // Shut ffmpeg up!
#endif
/*
* (c) 2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/