我想在跟踪程序捕获异常的一些信息时遇到问题。
我使用了以下功能:
extern "C" void log_backtrace()
{
// Dump the callstack
int callstack[128];
int frames = backtrace((void**) callstack, 128);
char** strs = backtrace_symbols((void**) callstack, frames);
for (int i = 1; i < frames; ++i)
{
char functionSymbol[64*1024];
char moduleName [64*1024];
int offset = 0;
sscanf(strs[i], "%*d %s %*s %s %*s %d", &moduleName, &functionSymbol, &offset);
int addr = callstack[i];
int validCppName;
char* functionName = abi::__cxa_demangle(functionSymbol, NULL, 0,
&validCppName);
if (validCppName == 0)
printf( "\t%8.8x — %s + %d\t\t(%s)\n", addr, functionName, offset, moduleName);
else
printf( "\t%8.8x — %s + %d\t\t(%s)\n", addr, functionSymbol, offset, moduleName);
if (functionName)
free(functionName);
}
free(strs);
}
输出如下:
20:48:44 [ERROR]tcp_client::connect() failed. error:Connection refused
00000001 — latte::Log::out_error(std::string const&) + 151 (valhalla)
001a6637 — latte::tcp_client::connect(boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> const&) + 307 (valhalla)
00000001 — valhalla::hall::start() + 388 (valhalla)
00204803 — main + 388 (valhalla)
00000001 — start + 52 (valhalla)
00143ae4 — 0x0 + 1 (???)
所有信息(命名空间,类名和方法名称)都很好。但唯一的问题是行号是错误的。
如何修复回溯中的行号?
答案 0 :(得分:12)
它们不是行号,而是函数开头的偏移量。有一个名为addr2line
的工具,它带有binutils,可以在给定调试符号的情况下将地址转换为行号。您可以在程序中调用此内容(pipe()
+ fork()
+ exec()
),也可以查看用于执行此操作的库。
在我的Linux系统上,addr2line在内部使用libbfd。根据我所看到的情况,它并没有很好的记录,但是通过addr2line源代码看起来相当容易。
答案 1 :(得分:1)
我遇到了同样的问题并寻求解决方案,但是我找不到任何东西。您已在帖子中标记了OSX,因此我假设您希望这个平台可以使用吗? OSX没有addr2line,至少在OSX版本10.8 / 10.9中没有。我把下面的代码放在一起(使用各种来源的代码片段),然后生成看起来像这样的回溯(它包括作为异常处理程序的前两个框架,如果你愿意,你可以跳过这个):
Caught SIGBUG: Bus error (bad memory access)
0 MyGame 0x342878 ExceptionHandler::PrintStackTrace() (in MyGame) (MacOSXEngine.cpp:89)
1 MyGame 0x342c4e ExceptionHandler::Handler(int, __siginfo*, void*) (in MyGame) (MacOSXEngine.cpp:232)
2 libsystem_platform.dylib 0x92beedeb 0x92beedeb
3 ??? 0xffffffff 0 + 4294967295
4 MyGame 0x48ae93 GlfwGraphicsSystem::Initialise(PiEngine::EmulationMode::T, PiGraphics::Orientation::T) (in MyGame) (GlfwGraphicsSystem.cpp:29)
5 MyGame 0x343f1f MacOSXEngine::Initialise() (in MyGame) (MacOSXEngine.cpp:581)
6 MyGame 0x342f8f main (in MyGame) (MacOSXEngine.cpp:304)
7 MyGame 0x3445 start (in MyGame) + 53
它只为执行模块中的帧生成函数+文件+行号。该代码仅适用于OSX,但可以适用于其他平台。将会出现没有覆盖的角落案例,但希望这是一个很好的起点。代码是:
namespace ExceptionHandler
{
char m_ExeFilename[ PATH_MAX ];
// Execute cmd store stdout into buf (up to bufSize).
int Execute( const char * cmd, char * buf, size_t bufSize )
{
char filename[ 512 ];
sprintf( filename, "%d.tmp", rand( ) );
if ( FILE * file = fopen( filename, "w" ) )
{
if ( FILE * ptr = popen( cmd, "r" ) )
{
while ( fgets( buf, bufSize, ptr ) != NULL )
{
fprintf( file, "%s", buf );
}
pclose( ptr );
}
fclose( file );
unlink( filename );
return 0;
}
return -1;
}
// Resolve symbol name and source location given the path to the executable and an address
int Addr2Line(char const * const program_name, void const * const addr, char * buff, size_t buffSize )
{
char addr2line_cmd[512] = {0};
sprintf( addr2line_cmd, "atos -d -o %.256s %p", program_name, addr );
return Execute( addr2line_cmd, buff, buffSize );
}
// Check if file exists.
bool FileExists( const char * filename )
{
if ( FILE * fh = fopen( filename, "r" ) )
{
fclose( fh );
return true;
}
return false;
}
// Print stack trace.
void PrintStackTrace( )
{
int trace_size = 0;
char ** messages = ( char ** )NULL;
static const size_t kMaxStackFrames = 64;
static void * stack_traces[ kMaxStackFrames ];
trace_size = backtrace( stack_traces, kMaxStackFrames );
messages = backtrace_symbols( stack_traces, trace_size );
for ( int i = 0; i < trace_size; ++i )
{
int stackLevel;
char filename[ 512 ];
uintptr_t address;
char symbol[ 512 ];
uintptr_t symbolOffset;
uintptr_t functionOffset;
bool symbolOffsetValid = false;
bool somethingValid = true;
if ( sscanf( messages[ i ], "%d%*[ \t]%s%*[ \t]%" SCNxPTR "%*[ \t]%" SCNxPTR "%*[ \t]+%*[ \t]%" SCNuPTR, &stackLevel, filename, &address, &symbolOffset, &functionOffset ) == 5 )
{
symbolOffsetValid = true;
}
else if ( sscanf( messages[ i ], "%d%*[ \t]%s%*[ \t]%" SCNxPTR "%*[ \t]%s%*[ \t]+%*[ \t]%" SCNuPTR, &stackLevel, filename, &address, symbol, &functionOffset ) == 5 )
{
}
else
{
somethingValid = false;
}
const size_t BUFF_SIZE = 4096;
char buff[ BUFF_SIZE ] = { '\0' };
if ( somethingValid )
{
if ( symbolOffsetValid && symbolOffset == 0 )
{
fprintf( stderr, "%3d %-32s %#16" PRIxPTR " %#" PRIxPTR " + %" PRIuPTR "\n", stackLevel, filename, address, symbolOffset, functionOffset );
}
else if ( FileExists( m_ExeFilename ) && Addr2Line( m_ExeFilename, stack_traces[ i ], buff, BUFF_SIZE) == 0 )
{
fprintf( stderr, "%3d %-32s %#16" PRIxPTR " %s", stackLevel, filename, address, buff );
}
else
{
fprintf( stderr, "%3d %-32s %#16" PRIxPTR " %#" PRIxPTR " + %" PRIuPTR "\n", stackLevel, filename, address, symbolOffset, functionOffset );
}
}
else
{
fprintf( stderr, "%s\n", messages[ i ] );
}
}
if (messages)
{
free( messages );
}
}
void Handler( int sig, siginfo_t * siginfo, void * context )
{
switch(sig)
{
case SIGSEGV:
fputs("Caught SIGSEGV: Segmentation Fault\n", stderr);
break;
case SIGBUS:
fputs("Caught SIGBUG: Bus error (bad memory access)\n", stderr);
break;
case SIGINT:
fputs("Caught SIGINT: Interactive attention signal, (usually ctrl+c)\n", stderr);
break;
case SIGFPE:
switch(siginfo->si_code)
{
case FPE_INTDIV:
fputs("Caught SIGFPE: (integer divide by zero)\n", stderr);
break;
case FPE_INTOVF:
fputs("Caught SIGFPE: (integer overflow)\n", stderr);
break;
case FPE_FLTDIV:
fputs("Caught SIGFPE: (floating-point divide by zero)\n", stderr);
break;
case FPE_FLTOVF:
fputs("Caught SIGFPE: (floating-point overflow)\n", stderr);
break;
case FPE_FLTUND:
fputs("Caught SIGFPE: (floating-point underflow)\n", stderr);
break;
case FPE_FLTRES:
fputs("Caught SIGFPE: (floating-point inexact result)\n", stderr);
break;
case FPE_FLTINV:
fputs("Caught SIGFPE: (floating-point invalid operation)\n", stderr);
break;
case FPE_FLTSUB:
fputs("Caught SIGFPE: (subscript out of range)\n", stderr);
break;
default:
fputs("Caught SIGFPE: Arithmetic Exception\n", stderr);
break;
}
break;
case SIGILL:
switch(siginfo->si_code)
{
case ILL_ILLOPC:
fputs("Caught SIGILL: (illegal opcode)\n", stderr);
break;
case ILL_ILLOPN:
fputs("Caught SIGILL: (illegal operand)\n", stderr);
break;
case ILL_ILLADR:
fputs("Caught SIGILL: (illegal addressing mode)\n", stderr);
break;
case ILL_ILLTRP:
fputs("Caught SIGILL: (illegal trap)\n", stderr);
break;
case ILL_PRVOPC:
fputs("Caught SIGILL: (privileged opcode)\n", stderr);
break;
case ILL_PRVREG:
fputs("Caught SIGILL: (privileged register)\n", stderr);
break;
case ILL_COPROC:
fputs("Caught SIGILL: (coprocessor error)\n", stderr);
break;
case ILL_BADSTK:
fputs("Caught SIGILL: (internal stack error)\n", stderr);
break;
default:
fputs("Caught SIGILL: Illegal Instruction\n", stderr);
break;
}
break;
case SIGTERM:
fputs("Caught SIGTERM: a termination request was sent to the program\n", stderr);
break;
case SIGABRT:
fputs("Caught SIGABRT: usually caused by an abort() or assert()\n", stderr);
break;
default:
break;
}
PrintStackTrace( );
fflush( stderr );
fflush( stdout );
_exit( 1 );
}
bool Initialise( const char * argv )
{
char path[ PATH_MAX ];
uint32_t size = sizeof( path );
if ( _NSGetExecutablePath( path, &size ) == 0 )
{
if ( ! realpath( path, m_ExeFilename ) )
{
strcpy( m_ExeFilename, path );
}
}
else
{
strcpy( m_ExeFilename, argv ? argv : "" );
}
struct sigaction sig_action = {};
sig_action.sa_sigaction = Handler;
sigemptyset(&sig_action.sa_mask);
sig_action.sa_flags = SA_SIGINFO;
int toCatch[ ] = {
SIGSEGV,
SIGBUS,
SIGFPE,
SIGINT,
SIGILL,
SIGTERM,
SIGABRT
};
bool okay = true;
for ( size_t toCatchIx = 0; toCatchIx < PiArraySize( toCatch ); ++toCatchIx )
{
okay &= sigaction( toCatch[ toCatchIx ], &sig_action, NULL ) == 0;
}
return okay;
}
}
int main( int argc, char ** argv )
{
argc = argc;
argv = argv;
ExceptionHandler::Initialise( argc > 0 ? argv[ 0 ] : NULL );
// Do something
return 0;
}