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, ®s );
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, ®s );
#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, ®s ) )
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, ®s );
#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 + -
显示快捷键?