samplnx.c

来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 867 行 · 第 1/2 页

C
867
字号
    WriteCodeLoad( ovl, name, kind );
    /* fake the address map - no segments here */
    WriteAddrMap( 1, FlatSeg, addr );
}


static void InternalError( char *str )
{
    Output( MsgArray[MSG_SAMPLE_2 - ERR_FIRST_MESSAGE] );
    Output( str );
    Output( "\n" );
    _exit( -1 );
}


#if defined( MD_x86 )

/* Handle profiler marks (hardcoded breakpoints with a string) */
static int ProcessMark( pid_t pid, user_regs_struct *regs )
{
    if( (regs->edx & 0xffff) != 0 ) {   /* this is a mark */
        char            buff[BUFF_SIZE];
        int             len = 0;
        seg_offset      where;

        /* read the mark string char by char */
        for( ;; ) {
            if( len >= (BUFF_SIZE - 1) )
                break;
            ReadMem( pid, &buff[len], regs->eax + len, 1 );
            if( buff[len] == '\0' )
                break;
            ++len;
        }
        buff[len] = '\0';
        where.segment = FlatSeg;
        where.offset = regs->eip;
        WriteMark( buff, where );
        return( TRUE );
    } else {
        dbg_printf( "hardcoded breakpoint was not a mark!\n" );
        return( FALSE );
    }
}

#endif

/* Handle breakpoints - we expect to see breakpoints in the dynamic linker
 * (which we set ourselves) as well as profiler marks (embedded in the
 * profiled application's code).
 */
static int ProcessBreakpoint( pid_t pid, u_long ip )
{
    static int  ld_state = RT_CONSISTENT;   // This ought to be per-pid
    int         ptrace_sig = 0;

#if defined( MD_x86 )
    user_regs_struct    regs;

    // on x86, when breakpoint was hit the EIP points to the next
    // instruction, so we must be careful
    ptrace( PTRACE_GETREGS, pid, NULL, &regs );

    if( ip == Rdebug.r_brk + sizeof( bp_t ) ) {
#elif defined( MD_ppc )
    if( ip == Rdebug.r_brk ) {
#endif
        bp_t                bp_opcode = BRK_POINT;
        int                 status;
        int                 ret;

        /* The dynamic linker breakpoint was hit, meaning that
         * libraries are being loaded or unloaded. This gets a bit
         * tricky because we must restore the original code that was
         * at the breakpoint and execute it, but we still want to
         * keep the breakpoint.
         */
        if( WriteMem( pid, &Old_ld_bp, Rdebug.r_brk, sizeof( bp_t ) ) != sizeof( bp_t ) )
            printf( "WriteMem() #1 failed\n" );
        ReadMem( pid, &Rdebug, (addr_off)DbgRdebug, sizeof( Rdebug ) );
        dbg_printf( "ld breakpoint hit, state is " );
        switch( Rdebug.r_state ) {
        case RT_ADD:
            dbg_printf( "RT_ADD\n" );
            AddLibrary( pid, Rdebug.r_map );
            ld_state = RT_ADD;
            break;
        case RT_DELETE:
            dbg_printf( "RT_DELETE\n" );
            ld_state = RT_DELETE;
            break;
        case RT_CONSISTENT:
            dbg_printf( "RT_CONSISTENT\n" );
            if( ld_state == RT_DELETE )
                RemoveLibrary( pid, Rdebug.r_map );
            ld_state = RT_CONSISTENT;
            break;
        default:
            dbg_printf( "error!\n" );
            break;
        }
#if defined( MD_x86 )
        regs.eip--;
        ptrace( PTRACE_SETREGS, pid, NULL, &regs );
#endif
        // unset breakpoint, single step, re-set breakpoint
        if( ptrace( PTRACE_SINGLESTEP, pid, NULL, (void *)ptrace_sig ) < 0 )
            perror( "ptrace()" );
        do {    // have to loop since SIGALRM can interrupt us here
            ret = waitpid( pid, &status, 0 );
        } while( (ret < 0) && (errno == EINTR) );
        if( ret == -1)
            perror( "waitpid()" );
        if( WriteMem( pid, &bp_opcode, Rdebug.r_brk, sizeof( bp_t ) ) != sizeof( bp_t ) )
            dbg_printf( "WriteMem() #2 failed with errno %d for pid %d, %d bytes (at %p)!\n", errno, pid, sizeof( bp_t ), Rdebug.r_brk );
        return( TRUE ); // indicate this was our breakpoint
    } else {
        dbg_printf( "Not an ld breakpoint, assuming mark\n" );
#if defined( MD_x86 )
        if( ProcessMark( pid, &regs ) )
            return( TRUE );
#endif
    }
    return( FALSE );
}


/*
 * Real time signal (SIGALRM) handler. All we really need to do is wake up
 * periodically to interrupt waitpid(), the signal handler need not do much
 * at all.
 */
static void alarm_handler( int signo )
{
    TimerTicked = TRUE;
}


/* Install periodic real time alarm signal */
static void InstSigHandler( int msec_period )
{
    struct sigaction    sigalrm;
    struct itimerval    timer;

    sigalrm.sa_handler = (void *)alarm_handler;
    sigemptyset( &sigalrm.sa_mask );
    sigalrm.sa_flags = 0;

    sigaction( SIGALRM, &sigalrm, NULL );

    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = msec_period * 1000;
    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = msec_period * 1000;

    if( setitimer( ITIMER_REAL, &timer, NULL ) ) {
        InternalError( MsgArray[MSG_SAMPLE_6 - ERR_FIRST_MESSAGE] );
    }
}


/*
 * Main sampler loop. We run the profiled application under the control of
 * ptrace(). The sampler installs a SIGALRM handler which ticks at the desired
 * rate. Whenever the SIGALRM interrupts our own waitpid(), we send a SIGSTOP
 * to the profiled app and when the child app notifies us of the SIGSTOP, we
 * remember the current execution point and continue the profiled app. Note
 * that we may miss some ticks but this is not a problem - the ticks don't
 * even need to be regular to provide usable results.
 */
static void SampleLoop( pid_t pid )
{
    static int          ptrace_sig = 0;
    static int          do_cont = TRUE;
    int                 status;
    user_regs_struct    regs;
    bool                sample_continue = TRUE;
    int                 ret;


    TimerTicked = FALSE;
    InstSigHandler( SleepTime );

    do {
        if( do_cont && ptrace( PTRACE_CONT, pid, NULL, (void *)ptrace_sig ) == -1)
            perror( "ptrace()" );
        ret = waitpid( pid, &status, 0 );
        if( (ret < 0) && (errno == EINTR) ) {
            /* did we get woken up by SIGALRM? */
            if( TimerTicked ) {
                TimerTicked = FALSE;
                /* interrupt child process - next waitpid() will see this */
                kill( pid, SIGSTOP );
            } else {
                dbg_printf( "who the hell interrupted waitpid()?\n" );
            }
            do_cont = FALSE;
            continue;
        }
        if( ret < 0 )
            perror( "waitpid()" );
        do_cont = TRUE;

        /* record current execution point */
#if defined( MD_x86 )
        ptrace( PTRACE_GETREGS, pid, NULL, &regs );
#elif defined( MD_ppc )
        regs.eip = ptrace( PTRACE_PEEKUSER, pid, REGSIZE * PT_NIP, NULL );
#endif
        if( WIFSTOPPED( status ) ) {
            /* If debuggee has dynamic section, try getting the r_debug struct
             * every time the child process stops. The r_debug data may not be
             * available immediately after the child process first loads.
             */
            if( !HaveRdebug && (DbgDyn != NULL) ) {
                if( Get_ld_info( pid, DbgDyn, &Rdebug, &DbgRdebug ) ) {
                    bp_t        opcode;

                    AddLibrary( pid, Rdebug.r_map );
                    HaveRdebug = TRUE;

                    /* Set a breakpoint in dynamic linker. That way we can be
                     * informed on dynamic library load/unload events.
                     */
                    ReadMem( pid, &Old_ld_bp, Rdebug.r_brk, sizeof( Old_ld_bp ) );
                    dbg_printf( "setting ld breakpoint at %p, old opcode was %X\n", Rdebug.r_brk, Old_ld_bp );
                    opcode = BRK_POINT;
                    WriteMem( pid, &opcode, Rdebug.r_brk, sizeof( opcode ) );
                }
            }

            sample_continue = FALSE;
            switch( (ptrace_sig = WSTOPSIG( status )) ) {
            case SIGSEGV:
                dbg_printf( "SIGSEGV at %p\n", regs.eip );
                sample_continue = TRUE;
                break;
            case SIGILL:
                dbg_printf( "SIGILL at %p\n", regs.eip );
                sample_continue = TRUE;
                break;
            case SIGABRT:
                dbg_printf( "SIGABRT at %p\n", regs.eip );
                sample_continue = TRUE;
                break;
            case SIGINT:
                dbg_printf( "SIGINT at %p\n", regs.eip );
                sample_continue = TRUE;
                break;
            case SIGTRAP:
                dbg_printf( "SIGTRAP at %p\n", regs.eip );
                if( ProcessBreakpoint( pid, regs.eip ) ) {
                    // don't pass on SIGTRAP if we expected this breakpoint
                    ptrace_sig = 0;
                }
                sample_continue = TRUE;
                break;
            case SIGSTOP:
                /* presumably we were behind this SIGSTOP */
                RecordSample( regs.eip, 1 );
                ptrace_sig = 0;
                sample_continue = TRUE;
                break;
            default:
                /* all other signals get passed down to the child and we let
                 * the child handle them (or not handle and die)
                 */
                sample_continue = TRUE;
                break;
            }
        } else if( WIFEXITED( status ) ) {
            dbg_printf( "WIFEXITED pid %d\n", pid );
            report();
            sample_continue = FALSE;
        } else if( WIFSIGNALED( status ) ) {
            dbg_printf( "WIFSIGNALED pid %d\n", pid );
            report();
            sample_continue = FALSE;
        }
    } while( sample_continue );
}


static int GetExeNameFromPid( pid_t pid, char *buffer, int max_len )
{
    char        procfile[24];
    int         len;

    sprintf( procfile, "/proc/%d/exe", pid );
    len = readlink( procfile, buffer, max_len );
    if( len < 0 )
        len = 0;
    buffer[len] = '\0';
    return( len );
}


void StartProg( char *cmd, char *prog, char *args )
{
    char            exe_name[PATH_MAX];
    pid_t           save_pgrp;
    pid_t           pid;
    int             status;

    MaxThread = 0;
    GrowArrays( 1 );
    HaveRdebug = FALSE;
    DbgDyn = NULL;
    OrigPGrp = getpgrp();
    Attached = TRUE;
    pid = Pid = SamplePid;

    /* allow attaching to existing process by pid */
    if( pid == 0 || ptrace( PTRACE_ATTACH, pid, NULL, NULL ) == -1 ) {
        int         num_args;
        size_t      len;
        char        **argv;

        Attached = FALSE;

        /* massage 'args' into argv format */
        ++args;     // first byte contains length - throwback to DOS days, needs fixing
        len = strlen( args );
        num_args = SplitParms( args, NULL, len );
        argv = alloca( (num_args + 2)  * sizeof( *argv ) );
        argv[SplitParms( args, &argv[1], len ) + 1] = NULL;
        argv[0] = prog;

        Output( MsgArray[MSG_SAMPLE_1 - ERR_FIRST_MESSAGE] );
        Output( prog );
        Output( "\n" );

        save_pgrp = getpgrp();
        setpgid( 0, OrigPGrp );
        pid = fork();
        if( pid == -1 )
            InternalError( MsgArray[MSG_SAMPLE_3 - ERR_FIRST_MESSAGE] );
        if( pid == 0 ) {
            int     rc;

            if( ptrace( PTRACE_TRACEME, 0, NULL, NULL ) < 0 ) {
                InternalError( MsgArray[MSG_SAMPLE_4 - ERR_FIRST_MESSAGE] );
            }
            dbg_printf( "executing '%s'\n", prog );
            for( rc = 0; argv[rc] != NULL; ++rc )
                dbg_printf( "argv[%d] = '%s'\n", rc, argv[rc] );

            rc = execve( prog, (char const * const *)argv, (char const * const *)environ );
            dbg_printf( "execve() failed, returned %d\n", rc );
            InternalError( MsgArray[MSG_SAMPLE_3 - ERR_FIRST_MESSAGE] );  // failsafe
        }
        setpgid( 0, save_pgrp );
        strcpy( exe_name, prog );
    } else if( pid ) {
        GetExeNameFromPid( pid, exe_name, PATH_MAX );
        Output( MsgArray[MSG_SAMPLE_1 - ERR_FIRST_MESSAGE] );
        Output( exe_name );
        Output( "\n" );
    }

    if( (pid != -1) && (pid != 0) ) {
        /* wait until it hits _start (upon execve) or
           gives us a SIGSTOP (if attached) */
        if( waitpid( pid, &status, 0 ) < 0 )
            goto fail;
        if( !WIFSTOPPED( status ) )
            goto fail;
        if( Attached ) {
            if( WSTOPSIG( status ) != SIGSTOP )
                goto fail;
        } else {
            if( WSTOPSIG( status ) != SIGTRAP )
                goto fail;
        }

        DbgDyn = GetDebuggeeDynSection( exe_name );
        errno = 0;
    }
    if( errno != 0 ) {
        pid = 0;
    }
    else {
        /* record information about main executable and initialize shared
         * library tracking
         */
        InitLibMap();
        CodeLoad( exe_name, 0, SAMP_MAIN_LOAD );
        SampleLoop( pid );
        FiniLibMap();
    }
    return;

fail:
    if( pid != 0 && pid != -1 ) {
        if( Attached ) {
            ptrace( PTRACE_DETACH, pid, NULL, NULL );
            Attached = FALSE;
        } else {
            ptrace( PTRACE_KILL, pid, NULL, NULL );
            waitpid( pid, &status, 0 );
        }
    }
    InternalError( MsgArray[MSG_SAMPLE_5 - ERR_FIRST_MESSAGE] );
}


void SysDefaultOptions( void )
{
}


void SysParseOptions( char c, char **cmd )
{
    char buff[2];

    switch( c ) {
    case 'r':
        SetTimerRate( cmd );
        break;
    case 'p':
        SetPid( cmd );
        break;
    default:
        Output( MsgArray[MSG_INVALID_OPTION - ERR_FIRST_MESSAGE] );
        buff[0] = c;
        buff[1] = '\0';
        Output( buff );
        Output( "\n" );
        fatal();
        break;
    }
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?