Files
itgmania212121/stepmania/src/archutils/Unix/BacktraceNames.cpp
T

438 lines
11 KiB
C++
Raw Normal View History

2004-03-12 05:15:32 +00:00
/* for dladdr: */
#define __USE_GNU
#include "global.h"
#include "BacktraceNames.h"
2004-04-05 05:22:32 +00:00
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
2004-03-12 05:15:32 +00:00
#include <unistd.h>
2004-04-05 05:22:32 +00:00
#include <cstring>
#include <cerrno>
2004-03-12 05:15:32 +00:00
#include "RageUtil.h"
#if defined(DARWIN)
#include "archutils/Darwin/Crash.h"
#endif
#if defined(HAVE_LIBIBERTY)
#include "libiberty.h"
/* This is in libiberty. Where is it declared? */
extern "C" {
char *cplus_demangle (const char *mangled, int options);
}
void BacktraceNames::Demangle()
{
char *f = cplus_demangle(Symbol, 0);
if(!f)
return;
Symbol = f;
free(f);
}
#elif defined(HAVE_CXA_DEMANGLE)
#include <cxxabi.h>
void BacktraceNames::Demangle()
{
/* demangle the name using __cxa_demangle() if needed */
if( Symbol.substr(0, 2) != "_Z" )
return;
int status = 0;
char *name = abi::__cxa_demangle( Symbol, 0, 0, &status );
if( name )
{
Symbol = name;
free( name );
return;
}
switch( status )
{
case -1:
fprintf( stderr, "Out of memory\n" );
break;
case -2:
fprintf( stderr, "Invalid mangled name: %s.\n", Symbol.c_str() );
break;
case -3:
fprintf( stderr, "Invalid arguments.\n" );
break;
default:
fprintf( stderr, "Unexpected __cxa_demangle status: %i", status );
break;
}
}
#else
void BacktraceNames::Demangle() { }
#endif
CString BacktraceNames::Format() const
{
CString ShortenedPath = File;
if( ShortenedPath != "" )
{
/* Abbreviate the module name. */
unsigned slash = ShortenedPath.rfind('/');
if( slash != ShortenedPath.npos )
ShortenedPath = ShortenedPath.substr(slash+1);
ShortenedPath = CString("(") + ShortenedPath + ")";
}
CString ret = ssprintf( "%08x: ", Address );
if( Symbol != "" )
ret += Symbol + " ";
ret += ShortenedPath;
return ret;
}
#if defined(BACKTRACE_LOOKUP_METHOD_DLADDR)
/* This version simply asks libdl, which is more robust. */
#include <dlfcn.h>
void BacktraceNames::FromAddr( const void *p )
{
Address = (int) p;
Dl_info di;
if( !dladdr(p, &di) )
return;
Symbol = di.dli_sname;
File = di.dli_fname;
Offset = (char*)(p)-(char*)di.dli_saddr;
}
2004-04-04 20:58:57 +00:00
#elif defined(BACKTRACE_LOOKUP_METHOD_DARWIN_DYLD)
#include <mach-o/dyld.h>
#include <mach-o/nlist.h>
/*
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>
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. */
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;
}
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 = (int) 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);
}
2004-03-12 05:15:32 +00:00
#elif defined(BACKTRACE_LOOKUP_METHOD_BACKTRACE_SYMBOLS)
/* This version parses backtrace_symbols(), an doesn't need libdl. */
#include <execinfo.h>
void BacktraceNames::FromAddr( const void *p )
{
Address = (int) p;
char **foo = backtrace_symbols(&p, 1);
if( foo == NULL )
return;
FromString( foo[0] );
free(foo);
}
/* "path(mangled name+offset) [address]" */
void BacktraceNames::FromString( CString s )
{
/* Hacky parser. I don't want to use regexes in the crash handler. */
CString MangledAndOffset, sAddress;
unsigned pos = 0;
while( pos < s.size() && s[pos] != '(' && s[pos] != '[' )
File += s[pos++];
TrimRight( File );
TrimLeft( File );
if( pos < s.size() && s[pos] == '(' )
{
pos++;
while( pos < s.size() && s[pos] != ')' )
MangledAndOffset += s[pos++];
}
if( MangledAndOffset != "" )
{
unsigned plus = MangledAndOffset.rfind('+');
if(plus == MangledAndOffset.npos)
{
Symbol = MangledAndOffset;
Offset = 0;
}
else
{
Symbol = MangledAndOffset.substr(0, plus);
CString str = MangledAndOffset.substr(plus);
if( sscanf(str, "%i", &Offset) != 1 )
Offset=0;
}
}
}
#elif defined(BACKTRACE_LOOKUP_METHOD_ATOS)
void BacktraceNames::FromAddr( const void *p )
{
int fds[2];
pid_t pid;
pid_t ppid = getpid(); /* Do this before fork()ing! */
2004-03-12 05:15:32 +00:00
Offset = 0;
Address = long(p);
if (pipe(fds) != 0)
{
fprintf(stderr, "FromAddr pipe() failed: %s\n", strerror(errno));
return;
}
pid = fork();
if (pid == -1)
{
fprintf(stderr, "FromAddr fork() failed: %s\n", strerror(errno));
return;
}
if (pid == 0)
{
close(fds[0]);
for (int fd = 3; fd < 1024; ++fd)
if (fd != fds[1])
close(fd);
2004-03-24 10:03:17 +00:00
dup2(fds[1], fileno(stdout));
2004-03-12 05:15:32 +00:00
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;
}
CStringArray mangledAndFile;
split(f, " ", mangledAndFile, true);
if (mangledAndFile.size() == 0)
return;
Symbol = mangledAndFile[0];
/* eg
* -[NSApplication run]
* +[SomeClass initialize]
*/
if (Symbol[0] == '-' || Symbol[0] == '+')
{
Symbol = mangledAndFile[0] + " " + mangledAndFile[1];
/* eg
* (crt.c:300)
* (AppKit)
*/
if (mangledAndFile.size() == 3)
{
File = mangledAndFile[2];
unsigned pos = File.find('(');
unsigned start = (pos == File.npos ? 0 : pos+1);
pos = File.rfind(')') - 1;
File = File.substr(start, pos);
}
return;
}
/* eg
* __start -> _start
* _SDL_main -> SDL_main
*/
if (Symbol[0] == '_')
Symbol = Symbol.substr(1);
/* eg, the full line:
* __Z1Ci (in a.out) (asmtest.cc:33)
* _main (in a.out) (asmtest.cc:52)
*/
if (mangledAndFile.size() > 3)
{
File = mangledAndFile[3];
unsigned pos = File.find('(');
unsigned start = (pos == File.npos ? 0 : pos+1);
pos = File.rfind(')') - 1;
File = File.substr(start, pos);
}
/* eg, the full line:
* _main (SDLMain.m:308)
* __Z8GameLoopv (crt.c:300)
*/
else if (mangledAndFile.size() == 3)
File = mangledAndFile[2].substr(0, mangledAndFile[2].rfind(')'));
}
#else
#warning Undefined BACKTRACE_LOOKUP_METHOD_*
void BacktraceNames::FromAddr( const void *p )
{
Address = long(p);
Offset = 0;
Symbol = "";
File = "";
}
#endif