3

プログラムのキャッチ例外に関する情報を追跡したいときに問題が発生しました。

以下の関数を使用しました。

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

すべての情報 (名前空間、クラス名、およびメソッド名) は適切です。しかし、唯一の問題は、行番号が間違っていることです。

バックトレースの行番号を修正するにはどうすればよいですか?

4

2 に答える 2

12

行番号ではなく、関数の先頭からのオフセットです。addr2lineデバッグシンボルを指定してアドレスを行番号に変換できる binutils に同梱されているツールがあります。プログラム内からこれを呼び出す ( pipe()+ fork()+ exec()) か、これを行うために使用するライブラリを調べることができます。

私の Linux システムでは、addr2line は内部でlibbfdを使用しています。私が見た限りでは十分に文書化されているわけではありませんが、 addr2line のソースからの例を見るとかなり簡単です。

于 2011-11-26T13:30:07.560 に答える
1

これと同じ問題があり、解決策を探しましたが、何も見つかりませんでした。投稿で OSX にタグを付けましたが、これが動作するプラットフォームであると仮定していますか? OSX には addr2line がありません。少なくとも OSX バージョン 10.8/10.9 にはありません。以下のコードを (さまざまなソースからのスニペットを使用して) まとめると、次のようなバックトレースが生成されます (例外ハンドラーである上部の 2 つのフレームが含まれています。必要に応じてこれをスキップできます)。

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;
}
于 2014-08-28T15:46:49.207 に答える