Initial commit.
This commit is contained in:
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
+747
-747
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
|
||||
@@ -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]();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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, ®s ) == -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, ®s ) == -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
|
||||
@@ -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
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user