如何修复C++中的回溯行号错误

e1xvtsh3  于 2023-02-14  发布在  其他
关注(0)|答案(3)|浏览(171)

我遇到了一个问题,而要跟踪一些信息的程序捕获异常.
我使用了下面的函数:

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      (???)

所有的信息(名称空间,类名和方法名)都很好。但唯一的问题是行号是错误的。
我怎样才能修正回溯中的行号?

amrnrhlw

amrnrhlw1#

它们不是行号,而是从函数开始的偏移量。有一个叫做addr2line的工具,它是随binutils一起提供的,可以在给定调试符号的情况下将地址转换成行号。你可以在你的程序中调用它(pipe() + fork() + exec()),或者查看它用来做这件事的库。
在我的Linux系统上,addr2line在内部使用libbfd来完成这个任务,虽然从我所看到的来看,它没有得到很好的记录,但是通过addr2line源代码中的示例可以很容易地看到它。

5cg8jx4n

5cg8jx4n2#

我也遇到了同样的问题,四处寻找解决方案,但是我什么也没找到。你在帖子中标记了OSX,所以我假设这是你想要的平台?OSX没有addr 2line,至少在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;
}
pqwbnv8z

pqwbnv8z3#

我意识到最初的问题已经有十多年的历史了--但我也有同样的问题,而且刚刚找到了答案。我在Mac上使用cmake为AppleClang生成makefile。
如果你使用cmake来编译你的可执行文件--你需要传入“-g”编译器选项--就像这样:
添加编译选项(-g)
然后需要使用lldb运行可执行文件,如下所示:
远程数据库
然后在lldb命令行中-你需要使用命令“run”,然后你需要发出回溯命令“bt”,然后你最终会得到你崩溃的行号。

相关问题