1999-02-19 03:21:36 +03:00
/*
* Copyright ( c ) 1991 , 1992 Paul Kranenburg < pk @ cs . few . eur . nl >
* Copyright ( c ) 1993 Branko Lankester < branko @ hacktic . nl >
* Copyright ( c ) 1993 , 1994 , 1995 , 1996 Rick Sladkey < jrs @ world . std . com >
1999-12-23 17:20:14 +03:00
* Copyright ( c ) 1996 - 1999 Wichert Akkerman < wichert @ cistron . nl >
1999-02-19 03:21:36 +03:00
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* $ Id $
*/
2000-02-18 18:36:12 +03:00
# include <sys/types.h>
1999-02-19 03:21:36 +03:00
# include "defs.h"
# include <signal.h>
# include <errno.h>
# include <sys/param.h>
# include <fcntl.h>
# include <sys/resource.h>
# include <sys/wait.h>
# include <sys/stat.h>
# include <pwd.h>
# include <grp.h>
# include <string.h>
2001-10-19 12:59:12 +04:00
# include <limits.h>
1999-02-19 03:21:36 +03:00
2001-10-10 03:47:38 +04:00
# if defined(IA64) && defined(LINUX)
# include <asm / ptrace_offsets.h>
# endif
2000-09-02 01:03:06 +04:00
# ifdef USE_PROCFS
# include <poll.h>
# endif
1999-02-19 03:21:36 +03:00
# ifdef SVR4
# include <sys/stropts.h>
1999-11-29 18:34:02 +03:00
# ifdef HAVE_MP_PROCFS
2001-07-10 17:48:44 +04:00
# ifdef HAVE_SYS_UIO_H
1999-08-30 03:15:07 +04:00
# include <sys/uio.h>
# endif
1999-02-19 03:21:36 +03:00
# endif
2001-07-10 17:48:44 +04:00
# endif
1999-02-19 03:21:36 +03:00
int debug = 0 , followfork = 0 , followvfork = 0 , interactive = 0 ;
int rflag = 0 , tflag = 0 , dtime = 0 , cflag = 0 ;
int iflag = 0 , xflag = 0 , qflag = 0 ;
int pflag_seen = 0 ;
2002-11-06 16:17:21 +03:00
/* Sometimes we want to print only succeeding syscalls. */
int not_failing_only = 0 ;
1999-02-19 03:21:36 +03:00
char * username = NULL ;
uid_t run_uid ;
gid_t run_gid ;
int acolumn = DEFAULT_ACOLUMN ;
int max_strlen = DEFAULT_STRLEN ;
char * outfname = NULL ;
FILE * outf ;
2002-12-18 07:16:10 +03:00
struct tcb * * tcbtab ;
unsigned int nprocs , tcbtabsize ;
1999-02-19 03:21:36 +03:00
char * progname ;
extern char * * environ ;
static int trace P ( ( void ) ) ;
static void cleanup P ( ( void ) ) ;
static void interrupt P ( ( int sig ) ) ;
static sigset_t empty_set , blocked_set ;
# ifdef HAVE_SIG_ATOMIC_T
static volatile sig_atomic_t interrupted ;
# else /* !HAVE_SIG_ATOMIC_T */
# ifdef __STDC__
static volatile int interrupted ;
# else /* !__STDC__ */
static int interrupted ;
# endif /* !__STDC__ */
# endif /* !HAVE_SIG_ATOMIC_T */
2000-09-02 01:03:06 +04:00
# ifdef USE_PROCFS
1999-02-19 03:21:36 +03:00
static struct tcb * pfd2tcb P ( ( int pfd ) ) ;
static void reaper P ( ( int sig ) ) ;
static void rebuild_pollv P ( ( void ) ) ;
2002-12-18 07:16:10 +03:00
static struct pollfd * pollv ;
1999-02-19 03:21:36 +03:00
# ifndef HAVE_POLLABLE_PROCFS
static void proc_poll_open P ( ( void ) ) ;
static void proc_poller P ( ( int pfd ) ) ;
struct proc_pollfd {
int fd ;
int revents ;
int pid ;
} ;
static int poller_pid ;
static int proc_poll_pipe [ 2 ] = { - 1 , - 1 } ;
# endif /* !HAVE_POLLABLE_PROCFS */
1999-11-29 18:34:02 +03:00
# ifdef HAVE_MP_PROCFS
1999-08-30 03:15:07 +04:00
# define POLLWANT POLLWRNORM
# else
# define POLLWANT POLLPRI
# endif
2000-09-02 01:03:06 +04:00
# endif /* USE_PROCFS */
1999-02-19 03:21:36 +03:00
static void
usage ( ofp , exitval )
FILE * ofp ;
int exitval ;
{
fprintf ( ofp , " \
usage : strace [ - dffhiqrtttTvVxx ] [ - a column ] [ - e expr ] . . . [ - o file ] \ n \
2003-01-24 07:31:23 +03:00
[ - p pid ] . . . [ - s strsize ] [ - u username ] [ - E var = val ] . . . \ n \
[ command [ arg . . . ] ] \ n \
or : strace - c [ - e expr ] . . . [ - O overhead ] [ - S sortby ] [ - E var = val ] . . . \ n \
[ command [ arg . . . ] ] \ n \
1999-02-19 03:21:36 +03:00
- c - - count time , calls , and errors for each syscall and report summary \ n \
- f - - follow forks , - ff - - with output into separate files \ n \
- F - - attempt to follow vforks , - h - - print help message \ n \
- i - - print instruction pointer at time of syscall \ n \
- q - - suppress messages about attaching , detaching , etc . \ n \
- r - - print relative timestamp , - t - - absolute timestamp , - tt - - with usecs \ n \
- T - - print time spent in each syscall , - V - - print version \ n \
- v - - verbose mode : print unabbreviated argv , stat , termio [ s ] , etc . args \ n \
- x - - print non - ascii strings in hex , - xx - - print all strings in hex \ n \
- a column - - alignment COLUMN for printing syscall results ( default % d ) \ n \
- e expr - - a qualifying expression : option = [ ! ] all or option = [ ! ] val1 [ , val2 ] . . . \ n \
options : trace , abbrev , verbose , raw , signal , read , or write \ n \
- o file - - send trace output to FILE instead of stderr \ n \
- O overhead - - set overhead for tracing syscalls to OVERHEAD usecs \ n \
- p pid - - trace process with process id PID , may be repeated \ n \
- s strsize - - limit length of print strings to STRSIZE chars ( default % d ) \ n \
- S sortby - - sort syscall counts by : time , calls , name , nothing ( default % s ) \ n \
- u username - - run command as username handling setuid and / or setgid \ n \
2003-01-24 07:31:23 +03:00
- E var = val - - put var = val in the environment for command \ n \
- E var - - remove var from the environment for command \ n \
" /* this is broken, so don't document it
2002-11-06 16:17:21 +03:00
- z - - print only succeeding syscalls \ n \
2003-01-24 07:31:23 +03:00
*/
, DEFAULT_ACOLUMN , DEFAULT_STRLEN , DEFAULT_SORTBY ) ;
1999-02-19 03:21:36 +03:00
exit ( exitval ) ;
}
# ifdef SVR4
# ifdef MIPS
void
foobar ( )
{
}
# endif /* MIPS */
# endif /* SVR4 */
int
main ( argc , argv )
int argc ;
char * argv [ ] ;
{
extern int optind ;
extern char * optarg ;
struct tcb * tcp ;
int c , pid = 0 ;
struct sigaction sa ;
static char buf [ BUFSIZ ] ;
2002-12-18 07:16:10 +03:00
/* Allocate the initial tcbtab. */
tcbtabsize = argc ; /* Surely enough for all -p args. */
tcbtab = ( struct tcb * * ) malloc ( tcbtabsize * sizeof tcbtab [ 0 ] ) ;
tcbtab [ 0 ] = ( struct tcb * ) calloc ( tcbtabsize , sizeof * tcbtab [ 0 ] ) ;
for ( tcp = tcbtab [ 0 ] ; tcp < & tcbtab [ 0 ] [ tcbtabsize ] ; + + tcp )
tcbtab [ tcp - tcbtab [ 0 ] ] = & tcbtab [ 0 ] [ tcp - tcbtab [ 0 ] ] ;
1999-02-19 03:21:36 +03:00
progname = argv [ 0 ] ;
outf = stderr ;
interactive = 1 ;
qualify ( " trace=all " ) ;
qualify ( " abbrev=all " ) ;
qualify ( " verbose=all " ) ;
qualify ( " signal=all " ) ;
set_sortby ( DEFAULT_SORTBY ) ;
set_personality ( DEFAULT_PERSONALITY ) ;
while ( ( c = getopt ( argc , argv ,
2003-01-24 07:31:23 +03:00
" +cdfFhiqrtTvVxza:e:o:O:p:s:S:u:E: " ) ) ! = EOF ) {
1999-02-19 03:21:36 +03:00
switch ( c ) {
case ' c ' :
cflag + + ;
dtime + + ;
break ;
case ' d ' :
debug + + ;
break ;
case ' f ' :
followfork + + ;
break ;
case ' F ' :
followvfork + + ;
break ;
case ' h ' :
usage ( stdout , 0 ) ;
break ;
case ' i ' :
iflag + + ;
break ;
case ' q ' :
qflag + + ;
break ;
case ' r ' :
rflag + + ;
tflag + + ;
break ;
case ' t ' :
tflag + + ;
break ;
case ' T ' :
dtime + + ;
break ;
case ' x ' :
xflag + + ;
break ;
case ' v ' :
qualify ( " abbrev=none " ) ;
break ;
case ' V ' :
2003-02-20 05:56:29 +03:00
printf ( " %s -- version %s \n " , PACKAGE_NAME , VERSION ) ;
1999-02-19 03:21:36 +03:00
exit ( 0 ) ;
break ;
2002-11-06 16:17:21 +03:00
case ' z ' :
not_failing_only = 1 ;
break ;
1999-02-19 03:21:36 +03:00
case ' a ' :
acolumn = atoi ( optarg ) ;
break ;
case ' e ' :
qualify ( optarg ) ;
break ;
case ' o ' :
outfname = strdup ( optarg ) ;
break ;
case ' O ' :
set_overhead ( atoi ( optarg ) ) ;
break ;
case ' p ' :
2003-01-24 07:31:23 +03:00
if ( ( pid = atoi ( optarg ) ) < = 0 ) {
1999-02-19 03:21:36 +03:00
fprintf ( stderr , " %s: Invalid process id: %s \n " ,
progname , optarg ) ;
break ;
}
if ( pid = = getpid ( ) ) {
1999-10-17 04:57:34 +04:00
fprintf ( stderr , " %s: I'm sorry, I can't let you do that, Dave. \n " , progname ) ;
1999-02-19 03:21:36 +03:00
break ;
}
if ( ( tcp = alloctcb ( pid ) ) = = NULL ) {
2003-01-24 07:31:23 +03:00
fprintf ( stderr , " %s: out of memory \n " ,
1999-02-19 03:21:36 +03:00
progname ) ;
exit ( 1 ) ;
}
tcp - > flags | = TCB_ATTACHED ;
pflag_seen + + ;
break ;
case ' s ' :
max_strlen = atoi ( optarg ) ;
break ;
case ' S ' :
set_sortby ( optarg ) ;
break ;
case ' u ' :
username = strdup ( optarg ) ;
break ;
2003-01-24 07:31:23 +03:00
case ' E ' :
if ( putenv ( optarg ) < 0 ) {
fprintf ( stderr , " %s: out of memory \n " ,
progname ) ;
exit ( 1 ) ;
}
break ;
1999-02-19 03:21:36 +03:00
default :
usage ( stderr , 1 ) ;
break ;
}
}
2003-11-12 00:24:23 +03:00
if ( optind = = argc & & ! pflag_seen )
usage ( stderr , 1 ) ;
1999-02-19 03:21:36 +03:00
/* See if they want to run as another user. */
if ( username ! = NULL ) {
struct passwd * pent ;
if ( getuid ( ) ! = 0 | | geteuid ( ) ! = 0 ) {
fprintf ( stderr ,
" %s: you must be root to use the -u option \n " ,
progname ) ;
exit ( 1 ) ;
}
if ( ( pent = getpwnam ( username ) ) = = NULL ) {
fprintf ( stderr , " %s: cannot find user `%s' \n " ,
progname , optarg ) ;
exit ( 1 ) ;
}
run_uid = pent - > pw_uid ;
run_gid = pent - > pw_gid ;
}
else {
run_uid = getuid ( ) ;
run_gid = getgid ( ) ;
}
# ifndef SVR4
setreuid ( geteuid ( ) , getuid ( ) ) ;
# endif
/* Check if they want to redirect the output. */
if ( outfname ) {
2001-08-03 15:43:35 +04:00
long f ;
2003-11-07 05:26:54 +03:00
/* See if they want to pipe the output. */
if ( outfname [ 0 ] = = ' | ' | | outfname [ 0 ] = = ' ! ' ) {
/*
* We can ' t do the < outfname > . PID funny business
* when using popen , so prohibit it .
*/
if ( followfork > 1 ) {
fprintf ( stderr , " \
% s : piping the output and - ff are mutually exclusive options \ n " ,
progname ) ;
exit ( 1 ) ;
}
if ( ( outf = popen ( outfname + 1 , " w " ) ) = = NULL ) {
fprintf ( stderr , " %s: can't popen '%s': %s \n " ,
progname , outfname + 1 ,
strerror ( errno ) ) ;
exit ( 1 ) ;
}
}
else if ( ( outf = fopen ( outfname , " w " ) ) = = NULL ) {
1999-02-19 03:21:36 +03:00
fprintf ( stderr , " %s: can't fopen '%s': %s \n " ,
progname , outfname , strerror ( errno ) ) ;
exit ( 1 ) ;
}
2001-08-03 15:43:35 +04:00
if ( ( f = fcntl ( fileno ( outf ) , F_GETFD ) ) < 0 ) {
perror ( " failed to get flags for outputfile " ) ;
exit ( 1 ) ;
}
if ( fcntl ( fileno ( outf ) , F_SETFD , f | FD_CLOEXEC ) < 0 ) {
perror ( " failed to set flags for outputfile " ) ;
exit ( 1 ) ;
}
1999-02-19 03:21:36 +03:00
}
# ifndef SVR4
setreuid ( geteuid ( ) , getuid ( ) ) ;
# endif
2003-11-07 05:26:54 +03:00
if ( ! outfname | | outfname [ 0 ] = = ' | ' | | outfname [ 0 ] = = ' ! ' )
1999-02-19 03:21:36 +03:00
setvbuf ( outf , buf , _IOLBF , BUFSIZ ) ;
2003-11-07 05:26:54 +03:00
if ( outfname & & optind < argc ) {
1999-02-19 03:21:36 +03:00
interactive = 0 ;
qflag = 1 ;
2003-06-03 05:35:20 +04:00
}
1999-02-19 03:21:36 +03:00
2002-12-18 07:16:10 +03:00
for ( c = 0 ; c < tcbtabsize ; c + + ) {
tcp = tcbtab [ c ] ;
1999-02-19 03:21:36 +03:00
/* Reinitialize the output since it may have changed. */
tcp - > outf = outf ;
if ( ! ( tcp - > flags & TCB_INUSE ) | | ! ( tcp - > flags & TCB_ATTACHED ) )
continue ;
2000-09-02 01:03:06 +04:00
# ifdef USE_PROCFS
1999-02-19 03:21:36 +03:00
if ( proc_open ( tcp , 1 ) < 0 ) {
fprintf ( stderr , " trouble opening proc file \n " ) ;
droptcb ( tcp ) ;
continue ;
}
2000-09-02 01:03:06 +04:00
# else /* !USE_PROCFS */
1999-02-19 03:21:36 +03:00
if ( ptrace ( PTRACE_ATTACH , tcp - > pid , ( char * ) 1 , 0 ) < 0 ) {
perror ( " attach: ptrace(PTRACE_ATTACH, ...) " ) ;
droptcb ( tcp ) ;
continue ;
}
2000-09-02 01:03:06 +04:00
# endif /* !USE_PROCFS */
1999-02-19 03:21:36 +03:00
if ( ! qflag )
fprintf ( stderr ,
" Process %u attached - interrupt to quit \n " ,
pid ) ;
}
2003-11-12 00:24:23 +03:00
if ( ! pflag_seen ) {
1999-02-19 03:21:36 +03:00
struct stat statbuf ;
char * filename ;
char pathname [ MAXPATHLEN ] ;
filename = argv [ optind ] ;
if ( strchr ( filename , ' / ' ) )
strcpy ( pathname , filename ) ;
# ifdef USE_DEBUGGING_EXEC
/*
* Debuggers customarily check the current directory
* first regardless of the path but doing that gives
* security geeks a panic attack .
*/
else if ( stat ( filename , & statbuf ) = = 0 )
strcpy ( pathname , filename ) ;
# endif /* USE_DEBUGGING_EXEC */
else {
char * path ;
int m , n , len ;
for ( path = getenv ( " PATH " ) ; path & & * path ; path + = m ) {
if ( strchr ( path , ' : ' ) ) {
n = strchr ( path , ' : ' ) - path ;
m = n + 1 ;
}
else
m = n = strlen ( path ) ;
if ( n = = 0 ) {
getcwd ( pathname , MAXPATHLEN ) ;
len = strlen ( pathname ) ;
}
else {
strncpy ( pathname , path , n ) ;
len = n ;
}
if ( len & & pathname [ len - 1 ] ! = ' / ' )
pathname [ len + + ] = ' / ' ;
strcpy ( pathname + len , filename ) ;
2003-06-03 11:18:19 +04:00
if ( stat ( pathname , & statbuf ) = = 0 & &
/* Accept only regular files
with some execute bits set .
XXX not perfect , might still fail */
S_ISREG ( statbuf . st_mode ) & &
( statbuf . st_mode & 0111 ) )
1999-02-19 03:21:36 +03:00
break ;
}
}
if ( stat ( pathname , & statbuf ) < 0 ) {
fprintf ( stderr , " %s: %s: command not found \n " ,
progname , filename ) ;
exit ( 1 ) ;
}
switch ( pid = fork ( ) ) {
case - 1 :
perror ( " strace: fork " ) ;
cleanup ( ) ;
exit ( 1 ) ;
break ;
case 0 : {
2000-09-02 01:03:06 +04:00
# ifdef USE_PROCFS
if ( outf ! = stderr ) close ( fileno ( outf ) ) ;
1999-02-19 03:21:36 +03:00
# ifdef MIPS
/* Kludge for SGI, see proc_open for details. */
sa . sa_handler = foobar ;
sa . sa_flags = 0 ;
sigemptyset ( & sa . sa_mask ) ;
sigaction ( SIGINT , & sa , NULL ) ;
# endif /* MIPS */
2000-09-02 01:03:06 +04:00
# ifndef FREEBSD
1999-02-19 03:21:36 +03:00
pause ( ) ;
2000-09-02 01:03:06 +04:00
# else /* FREEBSD */
kill ( getpid ( ) , SIGSTOP ) ; /* stop HERE */
2002-12-16 23:40:39 +03:00
# endif /* FREEBSD */
2000-09-02 01:03:06 +04:00
# else /* !USE_PROCFS */
2002-12-16 23:40:39 +03:00
if ( outf ! = stderr )
2000-07-05 20:05:39 +04:00
close ( fileno ( outf ) ) ;
2000-06-27 21:28:06 +04:00
1999-02-19 03:21:36 +03:00
if ( ptrace ( PTRACE_TRACEME , 0 , ( char * ) 1 , 0 ) < 0 ) {
perror ( " strace: ptrace(PTRACE_TRACEME, ...) " ) ;
return - 1 ;
}
if ( debug )
kill ( getpid ( ) , SIGSTOP ) ;
if ( username ! = NULL | | geteuid ( ) = = 0 ) {
uid_t run_euid = run_uid ;
gid_t run_egid = run_gid ;
if ( statbuf . st_mode & S_ISUID )
run_euid = statbuf . st_uid ;
if ( statbuf . st_mode & S_ISGID )
run_egid = statbuf . st_gid ;
/*
* It is important to set groups before we
* lose privileges on setuid .
*/
2000-05-01 05:53:59 +04:00
if ( username ! = NULL ) {
if ( initgroups ( username , run_gid ) < 0 ) {
perror ( " initgroups " ) ;
exit ( 1 ) ;
}
if ( setregid ( run_gid , run_egid ) < 0 ) {
perror ( " setregid " ) ;
exit ( 1 ) ;
}
if ( setreuid ( run_uid , run_euid ) < 0 ) {
perror ( " setreuid " ) ;
exit ( 1 ) ;
}
1999-02-19 03:21:36 +03:00
}
}
else
setreuid ( run_uid , run_uid ) ;
2000-09-02 01:03:06 +04:00
# endif /* !USE_PROCFS */
1999-02-19 03:21:36 +03:00
execv ( pathname , & argv [ optind ] ) ;
perror ( " strace: exec " ) ;
_exit ( 1 ) ;
break ;
}
default :
if ( ( tcp = alloctcb ( pid ) ) = = NULL ) {
fprintf ( stderr , " tcb table full \n " ) ;
cleanup ( ) ;
exit ( 1 ) ;
}
2000-09-02 01:03:06 +04:00
# ifdef USE_PROCFS
1999-02-19 03:21:36 +03:00
if ( proc_open ( tcp , 0 ) < 0 ) {
fprintf ( stderr , " trouble opening proc file \n " ) ;
cleanup ( ) ;
exit ( 1 ) ;
}
2000-09-02 01:03:06 +04:00
# endif /* USE_PROCFS */
# ifndef USE_PROCFS
1999-02-19 03:21:36 +03:00
fake_execve ( tcp , pathname , & argv [ optind ] , environ ) ;
2000-09-02 01:03:06 +04:00
# endif /* !USE_PROCFS */
1999-02-19 03:21:36 +03:00
break ;
}
}
sigemptyset ( & empty_set ) ;
sigemptyset ( & blocked_set ) ;
sa . sa_handler = SIG_IGN ;
sigemptyset ( & sa . sa_mask ) ;
sa . sa_flags = 0 ;
sigaction ( SIGTTOU , & sa , NULL ) ;
sigaction ( SIGTTIN , & sa , NULL ) ;
if ( interactive ) {
sigaddset ( & blocked_set , SIGHUP ) ;
sigaddset ( & blocked_set , SIGINT ) ;
sigaddset ( & blocked_set , SIGQUIT ) ;
sigaddset ( & blocked_set , SIGPIPE ) ;
sigaddset ( & blocked_set , SIGTERM ) ;
sa . sa_handler = interrupt ;
# ifdef SUNOS4
/* POSIX signals on sunos4.1 are a little broken. */
sa . sa_flags = SA_INTERRUPT ;
# endif /* SUNOS4 */
}
sigaction ( SIGHUP , & sa , NULL ) ;
sigaction ( SIGINT , & sa , NULL ) ;
sigaction ( SIGQUIT , & sa , NULL ) ;
sigaction ( SIGPIPE , & sa , NULL ) ;
sigaction ( SIGTERM , & sa , NULL ) ;
2000-09-02 01:03:06 +04:00
# ifdef USE_PROCFS
1999-02-19 03:21:36 +03:00
sa . sa_handler = reaper ;
sigaction ( SIGCHLD , & sa , NULL ) ;
2002-12-16 23:40:39 +03:00
# else
/* Make sure SIGCHLD has the default action so that waitpid
definitely works without losing track of children . The user
should not have given us a bogus state to inherit , but he might
have . Arguably we should detect SIG_IGN here and pass it on
to children , but probably noone really needs that . */
sa . sa_handler = SIG_DFL ;
sigaction ( SIGCHLD , & sa , NULL ) ;
2000-09-02 01:03:06 +04:00
# endif /* USE_PROCFS */
1999-02-19 03:21:36 +03:00
if ( trace ( ) < 0 )
exit ( 1 ) ;
cleanup ( ) ;
exit ( 0 ) ;
}
void
newoutf ( tcp )
struct tcb * tcp ;
{
char name [ MAXPATHLEN ] ;
FILE * fp ;
if ( outfname & & followfork > 1 ) {
sprintf ( name , " %s.%u " , outfname , tcp - > pid ) ;
# ifndef SVR4
setreuid ( geteuid ( ) , getuid ( ) ) ;
# endif
fp = fopen ( name , " w " ) ;
# ifndef SVR4
setreuid ( geteuid ( ) , getuid ( ) ) ;
# endif
if ( fp = = NULL ) {
perror ( " fopen " ) ;
return ;
}
tcp - > outf = fp ;
}
return ;
}
struct tcb *
alloctcb ( pid )
int pid ;
{
int i ;
struct tcb * tcp ;
2002-12-18 07:16:10 +03:00
for ( i = 0 ; i < tcbtabsize ; i + + ) {
tcp = tcbtab [ i ] ;
1999-02-19 03:21:36 +03:00
if ( ( tcp - > flags & TCB_INUSE ) = = 0 ) {
tcp - > pid = pid ;
tcp - > parent = NULL ;
tcp - > nchildren = 0 ;
2003-05-23 06:27:13 +04:00
tcp - > nzombies = 0 ;
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
# ifdef TCB_CLONE_THREAD
tcp - > nclone_threads = tcp - > nclone_detached = 0 ;
tcp - > nclone_waiting = 0 ;
# endif
1999-02-19 03:21:36 +03:00
tcp - > flags = TCB_INUSE | TCB_STARTUP ;
tcp - > outf = outf ; /* Initialise to current out file */
tcp - > stime . tv_sec = 0 ;
tcp - > stime . tv_usec = 0 ;
tcp - > pfd = - 1 ;
nprocs + + ;
return tcp ;
}
}
return NULL ;
}
2000-09-02 01:03:06 +04:00
# ifdef USE_PROCFS
1999-02-19 03:21:36 +03:00
int
proc_open ( tcp , attaching )
struct tcb * tcp ;
int attaching ;
{
char proc [ 32 ] ;
long arg ;
2000-09-02 01:03:06 +04:00
# ifdef SVR4
2001-10-19 12:59:12 +04:00
int i ;
sysset_t syscalls ;
1999-02-19 03:21:36 +03:00
sigset_t signals ;
fltset_t faults ;
2000-09-02 01:03:06 +04:00
# endif
1999-02-19 03:21:36 +03:00
# ifndef HAVE_POLLABLE_PROCFS
static int last_pfd ;
# endif
1999-11-29 18:34:02 +03:00
# ifdef HAVE_MP_PROCFS
1999-08-30 03:15:07 +04:00
/* Open the process pseudo-files in /proc. */
sprintf ( proc , " /proc/%d/ctl " , tcp - > pid ) ;
if ( ( tcp - > pfd = open ( proc , O_WRONLY | O_EXCL ) ) < 0 ) {
perror ( " strace: open( \" /proc/... \" , ...) " ) ;
return - 1 ;
}
if ( ( arg = fcntl ( tcp - > pfd , F_GETFD ) ) < 0 ) {
perror ( " F_GETFD " ) ;
return - 1 ;
}
if ( fcntl ( tcp - > pfd , F_SETFD , arg | FD_CLOEXEC ) < 0 ) {
perror ( " F_SETFD " ) ;
return - 1 ;
}
sprintf ( proc , " /proc/%d/status " , tcp - > pid ) ;
if ( ( tcp - > pfd_stat = open ( proc , O_RDONLY | O_EXCL ) ) < 0 ) {
perror ( " strace: open( \" /proc/... \" , ...) " ) ;
return - 1 ;
}
if ( ( arg = fcntl ( tcp - > pfd_stat , F_GETFD ) ) < 0 ) {
perror ( " F_GETFD " ) ;
return - 1 ;
}
if ( fcntl ( tcp - > pfd_stat , F_SETFD , arg | FD_CLOEXEC ) < 0 ) {
perror ( " F_SETFD " ) ;
return - 1 ;
}
sprintf ( proc , " /proc/%d/as " , tcp - > pid ) ;
if ( ( tcp - > pfd_as = open ( proc , O_RDONLY | O_EXCL ) ) < 0 ) {
perror ( " strace: open( \" /proc/... \" , ...) " ) ;
return - 1 ;
}
if ( ( arg = fcntl ( tcp - > pfd_as , F_GETFD ) ) < 0 ) {
perror ( " F_GETFD " ) ;
return - 1 ;
}
if ( fcntl ( tcp - > pfd_as , F_SETFD , arg | FD_CLOEXEC ) < 0 ) {
perror ( " F_SETFD " ) ;
return - 1 ;
}
# else
1999-02-19 03:21:36 +03:00
/* Open the process pseudo-file in /proc. */
2000-09-02 01:03:06 +04:00
# ifndef FREEBSD
1999-02-19 03:21:36 +03:00
sprintf ( proc , " /proc/%d " , tcp - > pid ) ;
if ( ( tcp - > pfd = open ( proc , O_RDWR | O_EXCL ) ) < 0 ) {
2000-09-02 01:03:06 +04:00
# else /* FREEBSD */
sprintf ( proc , " /proc/%d/mem " , tcp - > pid ) ;
if ( ( tcp - > pfd = open ( proc , O_RDWR ) ) < 0 ) {
# endif /* FREEBSD */
1999-02-19 03:21:36 +03:00
perror ( " strace: open( \" /proc/... \" , ...) " ) ;
return - 1 ;
}
1999-08-30 03:15:07 +04:00
if ( ( arg = fcntl ( tcp - > pfd , F_GETFD ) ) < 0 ) {
perror ( " F_GETFD " ) ;
return - 1 ;
}
if ( fcntl ( tcp - > pfd , F_SETFD , arg | FD_CLOEXEC ) < 0 ) {
perror ( " F_SETFD " ) ;
return - 1 ;
}
# endif
2000-09-02 01:03:06 +04:00
# ifdef FREEBSD
sprintf ( proc , " /proc/%d/regs " , tcp - > pid ) ;
if ( ( tcp - > pfd_reg = open ( proc , O_RDONLY ) ) < 0 ) {
perror ( " strace: open( \" /proc/.../regs \" , ...) " ) ;
return - 1 ;
}
if ( cflag ) {
sprintf ( proc , " /proc/%d/status " , tcp - > pid ) ;
if ( ( tcp - > pfd_status = open ( proc , O_RDONLY ) ) < 0 ) {
perror ( " strace: open( \" /proc/.../status \" , ...) " ) ;
return - 1 ;
}
} else
tcp - > pfd_status = - 1 ;
# endif /* FREEBSD */
1999-02-19 03:21:36 +03:00
rebuild_pollv ( ) ;
if ( ! attaching ) {
/*
* Wait for the child to pause . Because of a race
* condition we have to poll for the event .
*/
for ( ; ; ) {
1999-08-30 03:15:07 +04:00
if ( IOCTL_STATUS ( tcp ) < 0 ) {
1999-02-19 03:21:36 +03:00
perror ( " strace: PIOCSTATUS " ) ;
return - 1 ;
}
1999-08-30 03:15:07 +04:00
if ( tcp - > status . PR_FLAGS & PR_ASLEEP )
2000-09-02 01:03:06 +04:00
break ;
1999-02-19 03:21:36 +03:00
}
}
2000-09-02 01:03:06 +04:00
# ifndef FREEBSD
1999-02-19 03:21:36 +03:00
/* Stop the process so that we own the stop. */
2000-08-10 06:14:04 +04:00
if ( IOCTL ( tcp - > pfd , PIOCSTOP , ( char * ) NULL ) < 0 ) {
1999-02-19 03:21:36 +03:00
perror ( " strace: PIOCSTOP " ) ;
return - 1 ;
}
2002-12-16 23:40:39 +03:00
# endif
1999-02-19 03:21:36 +03:00
# ifdef PIOCSET
/* Set Run-on-Last-Close. */
arg = PR_RLC ;
1999-08-30 03:15:07 +04:00
if ( IOCTL ( tcp - > pfd , PIOCSET , & arg ) < 0 ) {
1999-02-19 03:21:36 +03:00
perror ( " PIOCSET PR_RLC " ) ;
return - 1 ;
}
/* Set or Reset Inherit-on-Fork. */
arg = PR_FORK ;
1999-08-30 03:15:07 +04:00
if ( IOCTL ( tcp - > pfd , followfork ? PIOCSET : PIOCRESET , & arg ) < 0 ) {
1999-02-19 03:21:36 +03:00
perror ( " PIOC{SET,RESET} PR_FORK " ) ;
return - 1 ;
}
# else /* !PIOCSET */
2002-12-16 23:40:39 +03:00
# ifndef FREEBSD
1999-02-19 03:21:36 +03:00
if ( ioctl ( tcp - > pfd , PIOCSRLC ) < 0 ) {
perror ( " PIOCSRLC " ) ;
return - 1 ;
}
if ( ioctl ( tcp - > pfd , followfork ? PIOCSFORK : PIOCRFORK ) < 0 ) {
perror ( " PIOC{S,R}FORK " ) ;
return - 1 ;
}
2000-09-02 01:03:06 +04:00
# else /* FREEBSD */
/* just unset the PF_LINGER flag for the Run-on-Last-Close. */
if ( ioctl ( tcp - > pfd , PIOCGFL , & arg ) < 0 ) {
perror ( " PIOCGFL " ) ;
return - 1 ;
}
arg & = ~ PF_LINGER ;
if ( ioctl ( tcp - > pfd , PIOCSFL , arg ) < 0 ) {
perror ( " PIOCSFL " ) ;
return - 1 ;
}
# endif /* FREEBSD */
1999-02-19 03:21:36 +03:00
# endif /* !PIOCSET */
2000-09-02 01:03:06 +04:00
# ifndef FREEBSD
2001-10-19 12:59:12 +04:00
/* Enable all syscall entries we care about. */
premptyset ( & syscalls ) ;
for ( i = 1 ; i < MAX_QUALS ; + + i ) {
if ( i > ( sizeof syscalls ) * CHAR_BIT ) break ;
if ( qual_flags [ i ] & QUAL_TRACE ) praddset ( & syscalls , i ) ;
}
praddset ( & syscalls , SYS_execve ) ;
if ( followfork ) {
praddset ( & syscalls , SYS_fork ) ;
# ifdef SYS_forkall
praddset ( & syscalls , SYS_forkall ) ;
# endif
2002-12-16 23:40:39 +03:00
# ifdef SYS_fork1
2001-10-19 12:59:12 +04:00
praddset ( & syscalls , SYS_fork1 ) ;
# endif
# ifdef SYS_rfork1
praddset ( & syscalls , SYS_rfork1 ) ;
# endif
# ifdef SYS_rforkall
praddset ( & syscalls , SYS_rforkall ) ;
# endif
}
if ( IOCTL ( tcp - > pfd , PIOCSENTRY , & syscalls ) < 0 ) {
1999-02-19 03:21:36 +03:00
perror ( " PIOCSENTRY " ) ;
return - 1 ;
}
2001-10-19 12:59:12 +04:00
/* Enable the syscall exits. */
if ( IOCTL ( tcp - > pfd , PIOCSEXIT , & syscalls ) < 0 ) {
1999-02-19 03:21:36 +03:00
perror ( " PIOSEXIT " ) ;
return - 1 ;
}
2001-10-19 12:59:12 +04:00
/* Enable signals we care about. */
premptyset ( & signals ) ;
for ( i = 1 ; i < MAX_QUALS ; + + i ) {
if ( i > ( sizeof signals ) * CHAR_BIT ) break ;
if ( qual_flags [ i ] & QUAL_SIGNAL ) praddset ( & signals , i ) ;
}
1999-08-30 03:15:07 +04:00
if ( IOCTL ( tcp - > pfd , PIOCSTRACE , & signals ) < 0 ) {
1999-02-19 03:21:36 +03:00
perror ( " PIOCSTRACE " ) ;
return - 1 ;
}
2001-10-19 12:59:12 +04:00
/* Enable faults we care about */
premptyset ( & faults ) ;
for ( i = 1 ; i < MAX_QUALS ; + + i ) {
if ( i > ( sizeof faults ) * CHAR_BIT ) break ;
if ( qual_flags [ i ] & QUAL_FAULT ) praddset ( & faults , i ) ;
}
1999-08-30 03:15:07 +04:00
if ( IOCTL ( tcp - > pfd , PIOCSFAULT , & faults ) < 0 ) {
1999-02-19 03:21:36 +03:00
perror ( " PIOCSFAULT " ) ;
return - 1 ;
}
2000-09-02 01:03:06 +04:00
# else /* FREEBSD */
/* set events flags. */
arg = S_SIG | S_SCE | S_SCX ;
if ( ioctl ( tcp - > pfd , PIOCBIS , arg ) < 0 ) {
perror ( " PIOCBIS " ) ;
return - 1 ;
}
# endif /* FREEBSD */
1999-02-19 03:21:36 +03:00
if ( ! attaching ) {
# ifdef MIPS
/*
* The SGI PRSABORT doesn ' t work for pause ( ) so
* we send it a caught signal to wake it up .
*/
kill ( tcp - > pid , SIGINT ) ;
# else /* !MIPS */
2002-12-16 23:40:39 +03:00
# ifdef PRSABORT
1999-02-19 03:21:36 +03:00
/* The child is in a pause(), abort it. */
1999-08-30 03:15:07 +04:00
arg = PRSABORT ;
if ( IOCTL ( tcp - > pfd , PIOCRUN , & arg ) < 0 ) {
1999-02-19 03:21:36 +03:00
perror ( " PIOCRUN " ) ;
return - 1 ;
}
2002-12-16 23:40:39 +03:00
# endif
2000-09-02 01:03:06 +04:00
# endif /* !MIPS*/
# ifdef FREEBSD
/* wake up the child if it received the SIGSTOP */
kill ( tcp - > pid , SIGCONT ) ;
2002-12-16 23:40:39 +03:00
# endif
1999-02-19 03:21:36 +03:00
for ( ; ; ) {
/* Wait for the child to do something. */
1999-08-30 03:15:07 +04:00
if ( IOCTL_WSTOP ( tcp ) < 0 ) {
1999-02-19 03:21:36 +03:00
perror ( " PIOCWSTOP " ) ;
return - 1 ;
}
1999-08-30 03:15:07 +04:00
if ( tcp - > status . PR_WHY = = PR_SYSENTRY ) {
2000-09-02 01:03:06 +04:00
tcp - > flags & = ~ TCB_INSYSCALL ;
get_scno ( tcp ) ;
if ( tcp - > scno = = SYS_execve )
1999-02-19 03:21:36 +03:00
break ;
}
/* Set it running: maybe execve will be next. */
2000-09-02 01:03:06 +04:00
# ifndef FREEBSD
1999-08-30 03:15:07 +04:00
arg = 0 ;
if ( IOCTL ( tcp - > pfd , PIOCRUN , & arg ) < 0 ) {
2000-09-02 01:03:06 +04:00
# else /* FREEBSD */
if ( IOCTL ( tcp - > pfd , PIOCRUN , 0 ) < 0 ) {
2002-12-16 23:40:39 +03:00
# endif /* FREEBSD */
1999-02-19 03:21:36 +03:00
perror ( " PIOCRUN " ) ;
return - 1 ;
}
2000-09-02 01:03:06 +04:00
# ifdef FREEBSD
/* handle the case where we "opened" the child before
it did the kill - STOP */
if ( tcp - > status . PR_WHY = = PR_SIGNALLED & &
tcp - > status . PR_WHAT = = SIGSTOP )
kill ( tcp - > pid , SIGCONT ) ;
2002-12-16 23:40:39 +03:00
# endif
1999-02-19 03:21:36 +03:00
}
2000-09-02 01:03:06 +04:00
# ifndef FREEBSD
1999-02-19 03:21:36 +03:00
}
2000-09-02 01:03:06 +04:00
# else /* FREEBSD */
} else {
2002-12-16 23:40:39 +03:00
if ( attaching < 2 ) {
2000-09-04 03:57:48 +04:00
/* We are attaching to an already running process.
* Try to figure out the state of the process in syscalls ,
* to handle the first event well .
* This is done by having a look at the " wchan " property of the
* process , which tells where it is stopped ( if it is ) . */
FILE * status ;
char wchan [ 20 ] ; /* should be enough */
2002-12-16 23:40:39 +03:00
2000-09-04 03:57:48 +04:00
sprintf ( proc , " /proc/%d/status " , tcp - > pid ) ;
status = fopen ( proc , " r " ) ;
if ( status & &
( fscanf ( status , " %*s %*d %*d %*d %*d %*d,%*d %*s %*d,%*d "
" %*d,%*d %*d,%*d %19s " , wchan ) = = 1 ) & &
strcmp ( wchan , " nochan " ) & & strcmp ( wchan , " spread " ) & &
strcmp ( wchan , " stopevent " ) ) {
/* The process is asleep in the middle of a syscall.
Fake the syscall entry event */
tcp - > flags & = ~ ( TCB_INSYSCALL | TCB_STARTUP ) ;
tcp - > status . PR_WHY = PR_SYSENTRY ;
trace_syscall ( tcp ) ;
}
if ( status )
fclose ( status ) ;
} /* otherwise it's a fork being followed */
2000-09-02 01:03:06 +04:00
}
# endif /* FREEBSD */
1999-02-19 03:21:36 +03:00
# ifndef HAVE_POLLABLE_PROCFS
if ( proc_poll_pipe [ 0 ] ! = - 1 )
proc_poller ( tcp - > pfd ) ;
else if ( nprocs > 1 ) {
proc_poll_open ( ) ;
proc_poller ( last_pfd ) ;
proc_poller ( tcp - > pfd ) ;
}
last_pfd = tcp - > pfd ;
# endif /* !HAVE_POLLABLE_PROCFS */
return 0 ;
}
2000-09-02 01:03:06 +04:00
# endif /* USE_PROCFS */
1999-02-19 03:21:36 +03:00
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
struct tcb *
1999-02-19 03:21:36 +03:00
pid2tcb ( pid )
int pid ;
{
int i ;
struct tcb * tcp ;
2002-12-18 07:16:10 +03:00
for ( i = 0 ; i < tcbtabsize ; i + + ) {
tcp = tcbtab [ i ] ;
1999-02-19 03:21:36 +03:00
if ( pid & & tcp - > pid ! = pid )
continue ;
if ( tcp - > flags & TCB_INUSE )
return tcp ;
}
return NULL ;
}
2000-09-02 01:03:06 +04:00
# ifdef USE_PROCFS
1999-02-19 03:21:36 +03:00
static struct tcb *
pfd2tcb ( pfd )
int pfd ;
{
int i ;
2003-01-10 22:55:28 +03:00
for ( i = 0 ; i < tcbtabsize ; i + + ) {
struct tcb * tcp = tcbtab [ i ] ;
1999-02-19 03:21:36 +03:00
if ( tcp - > pfd ! = pfd )
continue ;
if ( tcp - > flags & TCB_INUSE )
return tcp ;
}
return NULL ;
}
2000-09-02 01:03:06 +04:00
# endif /* USE_PROCFS */
1999-02-19 03:21:36 +03:00
void
droptcb ( tcp )
struct tcb * tcp ;
{
if ( tcp - > pid = = 0 )
return ;
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
# ifdef TCB_CLONE_THREAD
if ( tcp - > nclone_threads > 0 ) {
/* There are other threads left in this process, but this
is the one whose PID represents the whole process .
We need to keep this record around as a zombie until
all the threads die . */
tcp - > flags | = TCB_EXITING ;
return ;
}
# endif
1999-02-19 03:21:36 +03:00
nprocs - - ;
tcp - > pid = 0 ;
2002-04-01 21:48:02 +04:00
2003-01-10 23:14:20 +03:00
if ( tcp - > parent ! = NULL ) {
tcp - > parent - > nchildren - - ;
# ifdef TCB_CLONE_THREAD
if ( tcp - > flags & TCB_CLONE_DETACHED )
tcp - > parent - > nclone_detached - - ;
if ( tcp - > flags & TCB_CLONE_THREAD )
tcp - > parent - > nclone_threads - - ;
# endif
2003-05-23 06:27:13 +04:00
# ifdef TCB_CLONE_DETACHED
if ( ! ( tcp - > flags & TCB_CLONE_DETACHED ) )
# endif
tcp - > parent - > nzombies + + ;
2003-01-10 23:14:20 +03:00
tcp - > parent = NULL ;
}
tcp - > flags = 0 ;
1999-02-19 03:21:36 +03:00
if ( tcp - > pfd ! = - 1 ) {
close ( tcp - > pfd ) ;
tcp - > pfd = - 1 ;
2000-09-02 01:03:06 +04:00
# ifdef FREEBSD
if ( tcp - > pfd_reg ! = - 1 ) {
close ( tcp - > pfd_reg ) ;
tcp - > pfd_reg = - 1 ;
}
if ( tcp - > pfd_status ! = - 1 ) {
close ( tcp - > pfd_status ) ;
tcp - > pfd_status = - 1 ;
}
2002-12-16 23:40:39 +03:00
# endif /* !FREEBSD */
2000-09-02 01:03:06 +04:00
# ifdef USE_PROCFS
2003-01-10 23:14:20 +03:00
rebuild_pollv ( ) ; /* Note, flags needs to be cleared by now. */
1999-02-19 03:21:36 +03:00
# endif
}
2002-04-01 21:48:02 +04:00
2002-04-03 14:55:14 +04:00
if ( outfname & & followfork > 1 & & tcp - > outf )
1999-02-19 03:21:36 +03:00
fclose ( tcp - > outf ) ;
2002-04-01 21:48:02 +04:00
1999-02-19 03:21:36 +03:00
tcp - > outf = 0 ;
}
2000-09-02 01:03:06 +04:00
# ifndef USE_PROCFS
1999-02-19 03:21:36 +03:00
static int
resume ( tcp )
struct tcb * tcp ;
{
if ( tcp = = NULL )
return - 1 ;
if ( ! ( tcp - > flags & TCB_SUSPENDED ) ) {
fprintf ( stderr , " PANIC: pid %u not suspended \n " , tcp - > pid ) ;
return - 1 ;
}
tcp - > flags & = ~ TCB_SUSPENDED ;
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
# ifdef TCB_CLONE_THREAD
if ( tcp - > flags & TCB_CLONE_THREAD )
tcp - > parent - > nclone_waiting - - ;
# endif
1999-02-19 03:21:36 +03:00
if ( ptrace ( PTRACE_SYSCALL , tcp - > pid , ( char * ) 1 , 0 ) < 0 ) {
perror ( " resume: ptrace(PTRACE_SYSCALL, ...) " ) ;
return - 1 ;
}
if ( ! qflag )
fprintf ( stderr , " Process %u resumed \n " , tcp - > pid ) ;
return 0 ;
}
2000-09-02 01:03:06 +04:00
# endif /* !USE_PROCFS */
1999-02-19 03:21:36 +03:00
/* detach traced process; continue with sig */
static int
detach ( tcp , sig )
struct tcb * tcp ;
int sig ;
{
int error = 0 ;
2003-01-10 22:55:28 +03:00
# ifdef LINUX
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
int status , resumed ;
2003-01-10 22:55:28 +03:00
# endif
1999-02-19 03:21:36 +03:00
if ( tcp - > flags & TCB_BPTSET )
sig = SIGKILL ;
# ifdef LINUX
/*
* Linux wrongly insists the child be stopped
2002-12-16 23:42:50 +03:00
* before detaching . Arghh . We go through hoops
* to make a clean break of things .
1999-02-19 03:21:36 +03:00
*/
2002-12-16 23:42:50 +03:00
# if defined(SPARC)
# undef PTRACE_DETACH
# define PTRACE_DETACH PTRACE_SUNDETACH
# endif
1999-02-19 03:21:36 +03:00
if ( ( error = ptrace ( PTRACE_DETACH , tcp - > pid , ( char * ) 1 , sig ) ) = = 0 ) {
/* On a clear day, you can see forever. */
2002-12-16 23:42:50 +03:00
}
else if ( errno ! = ESRCH ) {
/* Shouldn't happen. */
perror ( " detach: ptrace(PTRACE_DETACH, ...) " ) ;
}
else if ( kill ( tcp - > pid , 0 ) < 0 ) {
if ( errno ! = ESRCH )
perror ( " detach: checking sanity " ) ;
}
else if ( kill ( tcp - > pid , SIGSTOP ) < 0 ) {
if ( errno ! = ESRCH )
perror ( " detach: stopping child " ) ;
}
else {
1999-02-19 03:21:36 +03:00
for ( ; ; ) {
2002-12-17 13:48:05 +03:00
# ifdef __WALL
if ( wait4 ( tcp - > pid , & status , __WALL , NULL ) < 0 ) {
if ( errno = = ECHILD ) /* Already gone. */
break ;
if ( errno ! = EINVAL ) {
1999-02-19 03:21:36 +03:00
perror ( " detach: waiting " ) ;
2002-12-17 13:48:05 +03:00
break ;
}
# endif /* __WALL */
/* No __WALL here. */
if ( waitpid ( tcp - > pid , & status , 0 ) < 0 ) {
if ( errno ! = ECHILD ) {
perror ( " detach: waiting " ) ;
break ;
}
# ifdef __WCLONE
/* If no processes, try clones. */
if ( wait4 ( tcp - > pid , & status , __WCLONE ,
NULL ) < 0 ) {
if ( errno ! = ECHILD )
perror ( " detach: waiting " ) ;
break ;
}
# endif /* __WCLONE */
}
# ifdef __WALL
1999-02-19 03:21:36 +03:00
}
2002-12-17 13:48:05 +03:00
# endif
1999-02-19 03:21:36 +03:00
if ( ! WIFSTOPPED ( status ) ) {
/* Au revoir, mon ami. */
break ;
}
if ( WSTOPSIG ( status ) = = SIGSTOP ) {
if ( ( error = ptrace ( PTRACE_DETACH ,
2002-12-16 23:42:50 +03:00
tcp - > pid , ( char * ) 1 , sig ) ) < 0 ) {
1999-02-19 03:21:36 +03:00
if ( errno ! = ESRCH )
perror ( " detach: ptrace(PTRACE_DETACH, ...) " ) ;
/* I died trying. */
}
break ;
}
if ( ( error = ptrace ( PTRACE_CONT , tcp - > pid , ( char * ) 1 ,
2002-12-16 23:42:50 +03:00
WSTOPSIG ( status ) = = SIGTRAP ?
0 : WSTOPSIG ( status ) ) ) < 0 ) {
1999-02-19 03:21:36 +03:00
if ( errno ! = ESRCH )
perror ( " detach: ptrace(PTRACE_CONT, ...) " ) ;
break ;
}
}
}
2002-12-16 23:42:50 +03:00
# endif /* LINUX */
1999-02-19 03:21:36 +03:00
# if defined(SUNOS4)
/* PTRACE_DETACH won't respect `sig' argument, so we post it here. */
if ( sig & & kill ( tcp - > pid , sig ) < 0 )
perror ( " detach: kill " ) ;
sig = 0 ;
if ( ( error = ptrace ( PTRACE_DETACH , tcp - > pid , ( char * ) 1 , sig ) ) < 0 )
perror ( " detach: ptrace(PTRACE_DETACH, ...) " ) ;
# endif /* SUNOS4 */
2000-09-02 01:03:06 +04:00
# ifndef USE_PROCFS
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
resumed = 0 ;
/* XXX This won't always be quite right (but it never was).
A waiter with argument 0 or < - 1 is waiting for any pid in
a particular pgrp , which this child might or might not be
in . The waiter will only wake up if it ' s argument is - 1
or if it ' s waiting for tcp - > pid ' s pgrp . It makes a
difference to wake up a waiter when there might be more
traced children , because it could get a false ECHILD
error . OTOH , if this was the last child in the pgrp , then
it ought to wake up and get ECHILD . We would have to
search the system for all pid ' s in the pgrp to be sure .
& & ( t - > waitpid = = - 1 | |
( t - > waitpid = = 0 & & getpgid ( tcp - > pid ) = = getpgid ( t - > pid ) )
| | ( t - > waitpid < 0 & & t - > waitpid = = - getpid ( t - > pid ) ) )
*/
if ( tcp - > parent & &
( tcp - > parent - > flags & TCB_SUSPENDED ) & &
( tcp - > parent - > waitpid < = 0 | | tcp - > parent - > waitpid = = tcp - > pid ) ) {
error = resume ( tcp - > parent ) ;
+ + resumed ;
}
# ifdef TCB_CLONE_THREAD
if ( tcp - > parent & & tcp - > parent - > nclone_waiting > 0 ) {
/* Some other threads of our parent are waiting too. */
unsigned int i ;
/* Resume all the threads that were waiting for this PID. */
for ( i = 0 ; i < tcbtabsize ; i + + ) {
struct tcb * t = tcbtab [ i ] ;
if ( t - > parent = = tcp - > parent & & t ! = tcp
& & ( ( t - > flags & ( TCB_CLONE_THREAD | TCB_SUSPENDED ) )
= = ( TCB_CLONE_THREAD | TCB_SUSPENDED ) )
& & t - > waitpid = = tcp - > pid ) {
error | = resume ( t ) ;
+ + resumed ;
}
}
if ( resumed = = 0 )
/* Noone was waiting for this PID in particular,
so now we might need to resume some wildcarders . */
for ( i = 0 ; i < tcbtabsize ; i + + ) {
struct tcb * t = tcbtab [ i ] ;
if ( t - > parent = = tcp - > parent & & t ! = tcp
& & ( ( t - > flags
& ( TCB_CLONE_THREAD | TCB_SUSPENDED ) )
= = ( TCB_CLONE_THREAD | TCB_SUSPENDED ) )
& & t - > waitpid < = 0
) {
error | = resume ( t ) ;
break ;
}
}
}
# endif
2000-09-02 01:03:06 +04:00
# endif /* !USE_PROCFS */
1999-02-19 03:21:36 +03:00
if ( ! qflag )
fprintf ( stderr , " Process %u detached \n " , tcp - > pid ) ;
droptcb ( tcp ) ;
return error ;
}
2000-09-02 01:03:06 +04:00
# ifdef USE_PROCFS
1999-02-19 03:21:36 +03:00
static void
reaper ( sig )
int sig ;
{
int pid ;
int status ;
while ( ( pid = waitpid ( - 1 , & status , WNOHANG ) ) > 0 ) {
#if 0
struct tcb * tcp ;
tcp = pid2tcb ( pid ) ;
if ( tcp )
droptcb ( tcp ) ;
# endif
}
}
2000-09-02 01:03:06 +04:00
# endif /* USE_PROCFS */
1999-02-19 03:21:36 +03:00
static void
cleanup ( )
{
int i ;
struct tcb * tcp ;
2002-12-18 07:16:10 +03:00
for ( i = 0 ; i < tcbtabsize ; i + + ) {
tcp = tcbtab [ i ] ;
1999-02-19 03:21:36 +03:00
if ( ! ( tcp - > flags & TCB_INUSE ) )
continue ;
if ( debug )
fprintf ( stderr ,
" cleanup: looking at pid %u \n " , tcp - > pid ) ;
if ( tcp_last & &
( ! outfname | | followfork < 2 | | tcp_last = = tcp ) ) {
tprintf ( " <unfinished ...> \n " ) ;
tcp_last = NULL ;
}
if ( tcp - > flags & TCB_ATTACHED )
detach ( tcp , 0 ) ;
else {
kill ( tcp - > pid , SIGCONT ) ;
kill ( tcp - > pid , SIGTERM ) ;
}
}
if ( cflag )
call_summary ( outf ) ;
}
static void
interrupt ( sig )
int sig ;
{
interrupted = 1 ;
}
# ifndef HAVE_STRERROR
2002-12-30 03:51:30 +03:00
# if !HAVE_DECL_SYS_ERRLIST
1999-02-19 03:21:36 +03:00
extern int sys_nerr ;
extern char * sys_errlist [ ] ;
2002-12-30 03:51:30 +03:00
# endif /* HAVE_DECL_SYS_ERRLIST */
1999-02-19 03:21:36 +03:00
const char *
strerror ( errno )
int errno ;
{
static char buf [ 64 ] ;
if ( errno < 1 | | errno > = sys_nerr ) {
sprintf ( buf , " Unknown error %d " , errno ) ;
return buf ;
}
return sys_errlist [ errno ] ;
}
# endif /* HAVE_STERRROR */
# ifndef HAVE_STRSIGNAL
2003-01-14 10:53:33 +03:00
# if defined HAVE_SYS_SIGLIST && !defined HAVE_DECL_SYS_SIGLIST
2002-12-30 03:51:30 +03:00
extern char * sys_siglist [ ] ;
1999-02-19 03:21:36 +03:00
# endif
2003-01-14 10:53:33 +03:00
# if defined HAVE_SYS__SIGLIST && !defined HAVE_DECL__SYS_SIGLIST
extern char * _sys_siglist [ ] ;
# endif
1999-02-19 03:21:36 +03:00
const char *
strsignal ( sig )
int sig ;
{
static char buf [ 64 ] ;
if ( sig < 1 | | sig > = NSIG ) {
sprintf ( buf , " Unknown signal %d " , sig ) ;
return buf ;
}
# ifdef HAVE__SYS_SIGLIST
return _sys_siglist [ sig ] ;
# else
return sys_siglist [ sig ] ;
# endif
}
# endif /* HAVE_STRSIGNAL */
2000-09-02 01:03:06 +04:00
# ifdef USE_PROCFS
1999-02-19 03:21:36 +03:00
static void
rebuild_pollv ( )
{
int i , j ;
2002-12-18 07:16:10 +03:00
if ( pollv ! = NULL )
free ( pollv ) ;
2003-01-10 23:05:56 +03:00
pollv = ( struct pollfd * ) malloc ( nprocs * sizeof pollv [ 0 ] ) ;
2002-12-18 07:16:10 +03:00
if ( pollv = = NULL ) {
fprintf ( stderr , " strace: out of memory for poll vector \n " ) ;
exit ( 1 ) ;
}
2003-01-10 22:55:28 +03:00
for ( i = j = 0 ; i < tcbtabsize ; i + + ) {
struct tcb * tcp = tcbtab [ i ] ;
1999-02-19 03:21:36 +03:00
if ( ! ( tcp - > flags & TCB_INUSE ) )
continue ;
pollv [ j ] . fd = tcp - > pfd ;
1999-08-30 03:15:07 +04:00
pollv [ j ] . events = POLLWANT ;
1999-02-19 03:21:36 +03:00
j + + ;
}
if ( j ! = nprocs ) {
fprintf ( stderr , " strace: proc miscount \n " ) ;
exit ( 1 ) ;
}
}
# ifndef HAVE_POLLABLE_PROCFS
static void
proc_poll_open ( )
{
int arg ;
int i ;
if ( pipe ( proc_poll_pipe ) < 0 ) {
perror ( " pipe " ) ;
exit ( 1 ) ;
}
for ( i = 0 ; i < 2 ; i + + ) {
if ( ( arg = fcntl ( proc_poll_pipe [ i ] , F_GETFD ) ) < 0 ) {
perror ( " F_GETFD " ) ;
exit ( 1 ) ;
}
if ( fcntl ( proc_poll_pipe [ i ] , F_SETFD , arg | FD_CLOEXEC ) < 0 ) {
perror ( " F_SETFD " ) ;
exit ( 1 ) ;
}
}
}
static int
proc_poll ( pollv , nfds , timeout )
struct pollfd * pollv ;
int nfds ;
int timeout ;
{
int i ;
int n ;
struct proc_pollfd pollinfo ;
if ( ( n = read ( proc_poll_pipe [ 0 ] , & pollinfo , sizeof ( pollinfo ) ) ) < 0 )
return n ;
if ( n ! = sizeof ( struct proc_pollfd ) ) {
fprintf ( stderr , " panic: short read: %d \n " , n ) ;
exit ( 1 ) ;
}
for ( i = 0 ; i < nprocs ; i + + ) {
if ( pollv [ i ] . fd = = pollinfo . fd )
pollv [ i ] . revents = pollinfo . revents ;
else
pollv [ i ] . revents = 0 ;
}
poller_pid = pollinfo . pid ;
return 1 ;
}
static void
wakeup_handler ( sig )
int sig ;
{
}
static void
proc_poller ( pfd )
int pfd ;
{
struct proc_pollfd pollinfo ;
struct sigaction sa ;
sigset_t blocked_set , empty_set ;
int i ;
int n ;
struct rlimit rl ;
2000-09-02 01:03:06 +04:00
# ifdef FREEBSD
struct procfs_status pfs ;
# endif /* FREEBSD */
1999-02-19 03:21:36 +03:00
switch ( fork ( ) ) {
case - 1 :
perror ( " fork " ) ;
_exit ( 0 ) ;
case 0 :
break ;
default :
return ;
}
sa . sa_handler = interactive ? SIG_DFL : SIG_IGN ;
sa . sa_flags = 0 ;
sigemptyset ( & sa . sa_mask ) ;
sigaction ( SIGHUP , & sa , NULL ) ;
sigaction ( SIGINT , & sa , NULL ) ;
sigaction ( SIGQUIT , & sa , NULL ) ;
sigaction ( SIGPIPE , & sa , NULL ) ;
sigaction ( SIGTERM , & sa , NULL ) ;
sa . sa_handler = wakeup_handler ;
sigaction ( SIGUSR1 , & sa , NULL ) ;
sigemptyset ( & blocked_set ) ;
sigaddset ( & blocked_set , SIGUSR1 ) ;
sigprocmask ( SIG_BLOCK , & blocked_set , NULL ) ;
sigemptyset ( & empty_set ) ;
if ( getrlimit ( RLIMIT_NOFILE , & rl ) < 0 ) {
perror ( " getrlimit(RLIMIT_NOFILE, ...) " ) ;
_exit ( 0 ) ;
}
n = rl . rlim_cur ;
for ( i = 0 ; i < n ; i + + ) {
if ( i ! = pfd & & i ! = proc_poll_pipe [ 1 ] )
close ( i ) ;
}
pollinfo . fd = pfd ;
pollinfo . pid = getpid ( ) ;
for ( ; ; ) {
2000-09-02 01:03:06 +04:00
# ifndef FREEBSD
if ( ioctl ( pfd , PIOCWSTOP , NULL ) < 0 )
# else /* FREEBSD */
if ( ioctl ( pfd , PIOCWSTOP , & pfs ) < 0 )
# endif /* FREEBSD */
1999-08-30 03:15:07 +04:00
{
1999-02-19 03:21:36 +03:00
switch ( errno ) {
case EINTR :
continue ;
case EBADF :
pollinfo . revents = POLLERR ;
break ;
case ENOENT :
pollinfo . revents = POLLHUP ;
break ;
default :
perror ( " proc_poller: PIOCWSTOP " ) ;
}
write ( proc_poll_pipe [ 1 ] , & pollinfo , sizeof ( pollinfo ) ) ;
_exit ( 0 ) ;
}
1999-08-30 03:15:07 +04:00
pollinfo . revents = POLLWANT ;
1999-02-19 03:21:36 +03:00
write ( proc_poll_pipe [ 1 ] , & pollinfo , sizeof ( pollinfo ) ) ;
sigsuspend ( & empty_set ) ;
}
}
# endif /* !HAVE_POLLABLE_PROCFS */
static int
choose_pfd ( )
{
int i , j ;
struct tcb * tcp ;
static int last ;
if ( followfork < 2 & &
1999-08-30 03:15:07 +04:00
last < nprocs & & ( pollv [ last ] . revents & POLLWANT ) ) {
1999-02-19 03:21:36 +03:00
/*
* The previous process is ready to run again . We ' ll
* let it do so if it is currently in a syscall . This
* heuristic improves the readability of the trace .
*/
tcp = pfd2tcb ( pollv [ last ] . fd ) ;
if ( tcp & & ( tcp - > flags & TCB_INSYSCALL ) )
return pollv [ last ] . fd ;
}
for ( i = 0 ; i < nprocs ; i + + ) {
/* Let competing children run round robin. */
j = ( i + last + 1 ) % nprocs ;
if ( pollv [ j ] . revents & ( POLLHUP | POLLERR ) ) {
tcp = pfd2tcb ( pollv [ j ] . fd ) ;
if ( ! tcp ) {
fprintf ( stderr , " strace: lost proc \n " ) ;
exit ( 1 ) ;
}
droptcb ( tcp ) ;
return - 1 ;
}
1999-08-30 03:15:07 +04:00
if ( pollv [ j ] . revents & POLLWANT ) {
1999-02-19 03:21:36 +03:00
last = j ;
return pollv [ j ] . fd ;
}
}
fprintf ( stderr , " strace: nothing ready \n " ) ;
exit ( 1 ) ;
}
static int
trace ( )
{
1999-11-26 16:11:29 +03:00
# ifdef POLL_HACK
2002-05-21 15:24:18 +04:00
struct tcb * in_syscall = NULL ;
1999-11-26 16:11:29 +03:00
# endif
1999-02-19 03:21:36 +03:00
struct tcb * tcp ;
int pfd ;
int what ;
int ioctl_result = 0 , ioctl_errno = 0 ;
1999-08-30 03:15:07 +04:00
long arg ;
1999-02-19 03:21:36 +03:00
for ( ; ; ) {
if ( interactive )
sigprocmask ( SIG_SETMASK , & empty_set , NULL ) ;
if ( nprocs = = 0 )
break ;
switch ( nprocs ) {
case 1 :
# ifndef HAVE_POLLABLE_PROCFS
if ( proc_poll_pipe [ 0 ] = = - 1 ) {
# endif
tcp = pid2tcb ( 0 ) ;
if ( ! tcp )
continue ;
pfd = tcp - > pfd ;
if ( pfd = = - 1 )
continue ;
break ;
# ifndef HAVE_POLLABLE_PROCFS
}
/* fall through ... */
# endif /* !HAVE_POLLABLE_PROCFS */
default :
# ifdef HAVE_POLLABLE_PROCFS
1999-11-26 16:11:29 +03:00
# ifdef POLL_HACK
/* On some systems (e.g. UnixWare) we get too much ugly
" unfinished... " stuff when multiple proceses are in
syscalls . Here ' s a nasty hack */
2002-12-16 23:40:39 +03:00
1999-11-26 16:11:29 +03:00
if ( in_syscall ) {
struct pollfd pv ;
tcp = in_syscall ;
in_syscall = NULL ;
pv . fd = tcp - > pfd ;
pv . events = POLLWANT ;
if ( ( what = poll ( & pv , 1 , 1 ) ) < 0 ) {
if ( interrupted )
return 0 ;
continue ;
}
else if ( what = = 1 & & pv . revents & POLLWANT ) {
goto FOUND ;
}
}
# endif
1999-02-19 03:21:36 +03:00
if ( poll ( pollv , nprocs , INFTIM ) < 0 ) {
if ( interrupted )
return 0 ;
continue ;
}
# else /* !HAVE_POLLABLE_PROCFS */
if ( proc_poll ( pollv , nprocs , INFTIM ) < 0 ) {
if ( interrupted )
return 0 ;
continue ;
}
# endif /* !HAVE_POLLABLE_PROCFS */
pfd = choose_pfd ( ) ;
if ( pfd = = - 1 )
continue ;
break ;
}
/* Look up `pfd' in our table. */
if ( ( tcp = pfd2tcb ( pfd ) ) = = NULL ) {
fprintf ( stderr , " unknown pfd: %u \n " , pfd ) ;
exit ( 1 ) ;
}
2002-05-23 15:02:22 +04:00
# ifdef POLL_HACK
1999-11-26 16:11:29 +03:00
FOUND :
2002-05-23 15:02:22 +04:00
# endif
1999-02-19 03:21:36 +03:00
/* Get the status of the process. */
if ( ! interrupted ) {
2000-09-02 01:03:06 +04:00
# ifndef FREEBSD
1999-08-30 03:15:07 +04:00
ioctl_result = IOCTL_WSTOP ( tcp ) ;
2000-09-02 01:03:06 +04:00
# else /* FREEBSD */
/* Thanks to some scheduling mystery, the first poller
sometimes waits for the already processed end of fork
event . Doing a non blocking poll here solves the problem . */
if ( proc_poll_pipe [ 0 ] ! = - 1 )
ioctl_result = IOCTL_STATUS ( tcp ) ;
else
ioctl_result = IOCTL_WSTOP ( tcp ) ;
2002-12-16 23:40:39 +03:00
# endif /* FREEBSD */
1999-02-19 03:21:36 +03:00
ioctl_errno = errno ;
# ifndef HAVE_POLLABLE_PROCFS
if ( proc_poll_pipe [ 0 ] ! = - 1 ) {
if ( ioctl_result < 0 )
kill ( poller_pid , SIGKILL ) ;
else
kill ( poller_pid , SIGUSR1 ) ;
}
# endif /* !HAVE_POLLABLE_PROCFS */
}
if ( interrupted )
return 0 ;
if ( interactive )
sigprocmask ( SIG_BLOCK , & blocked_set , NULL ) ;
if ( ioctl_result < 0 ) {
/* Find out what happened if it failed. */
switch ( ioctl_errno ) {
case EINTR :
case EBADF :
continue ;
2000-09-02 01:03:06 +04:00
# ifdef FREEBSD
case ENOTTY :
2002-12-16 23:40:39 +03:00
# endif
1999-02-19 03:21:36 +03:00
case ENOENT :
droptcb ( tcp ) ;
continue ;
default :
perror ( " PIOCWSTOP " ) ;
exit ( 1 ) ;
}
}
2000-09-04 03:57:48 +04:00
# ifdef FREEBSD
if ( ( tcp - > flags & TCB_STARTUP ) & & ( tcp - > status . PR_WHY = = PR_SYSEXIT ) ) {
/* discard first event for a syscall we never entered */
IOCTL ( tcp - > pfd , PIOCRUN , 0 ) ;
continue ;
}
2002-12-16 23:40:39 +03:00
# endif
1999-02-19 03:21:36 +03:00
/* clear the just started flag */
tcp - > flags & = ~ TCB_STARTUP ;
/* set current output file */
outf = tcp - > outf ;
if ( cflag ) {
struct timeval stime ;
2000-09-02 01:03:06 +04:00
# ifdef FREEBSD
char buf [ 1024 ] ;
int len ;
if ( ( len = pread ( tcp - > pfd_status , buf , sizeof ( buf ) - 1 , 0 ) ) > 0 ) {
buf [ len ] = ' \0 ' ;
sscanf ( buf ,
" %*s %*d %*d %*d %*d %*d,%*d %*s %*d,%*d %*d,%*d %ld,%ld " ,
& stime . tv_sec , & stime . tv_usec ) ;
} else
stime . tv_sec = stime . tv_usec = 0 ;
2002-12-16 23:40:39 +03:00
# else /* !FREEBSD */
1999-02-19 03:21:36 +03:00
stime . tv_sec = tcp - > status . pr_stime . tv_sec ;
stime . tv_usec = tcp - > status . pr_stime . tv_nsec / 1000 ;
2000-09-02 01:03:06 +04:00
# endif /* !FREEBSD */
1999-02-19 03:21:36 +03:00
tv_sub ( & tcp - > dtime , & stime , & tcp - > stime ) ;
tcp - > stime = stime ;
}
1999-08-30 03:15:07 +04:00
what = tcp - > status . PR_WHAT ;
switch ( tcp - > status . PR_WHY ) {
2000-09-02 01:03:06 +04:00
# ifndef FREEBSD
1999-02-19 03:21:36 +03:00
case PR_REQUESTED :
1999-08-30 03:15:07 +04:00
if ( tcp - > status . PR_FLAGS & PR_ASLEEP ) {
tcp - > status . PR_WHY = PR_SYSENTRY ;
1999-02-19 03:21:36 +03:00
if ( trace_syscall ( tcp ) < 0 ) {
fprintf ( stderr , " syscall trouble \n " ) ;
exit ( 1 ) ;
}
}
break ;
2000-09-02 01:03:06 +04:00
# endif /* !FREEBSD */
1999-02-19 03:21:36 +03:00
case PR_SYSENTRY :
1999-11-26 16:11:29 +03:00
# ifdef POLL_HACK
in_syscall = tcp ;
# endif
1999-02-19 03:21:36 +03:00
case PR_SYSEXIT :
if ( trace_syscall ( tcp ) < 0 ) {
fprintf ( stderr , " syscall trouble \n " ) ;
exit ( 1 ) ;
}
break ;
case PR_SIGNALLED :
if ( ! cflag & & ( qual_flags [ what ] & QUAL_SIGNAL ) ) {
printleader ( tcp ) ;
tprintf ( " --- %s (%s) --- " ,
1999-03-30 03:23:13 +04:00
signame ( what ) , strsignal ( what ) ) ;
1999-02-19 03:21:36 +03:00
printtrailer ( tcp ) ;
2001-10-18 19:13:53 +04:00
# ifdef PR_INFO
if ( tcp - > status . PR_INFO . si_signo = = what ) {
printleader ( tcp ) ;
tprintf ( " siginfo= " ) ;
printsiginfo ( & tcp - > status . PR_INFO , 1 ) ;
printtrailer ( tcp ) ;
}
# endif
1999-02-19 03:21:36 +03:00
}
break ;
case PR_FAULTED :
if ( ! cflag & & ( qual_flags [ what ] & QUAL_FAULT ) ) {
printleader ( tcp ) ;
tprintf ( " === FAULT %d === " , what ) ;
printtrailer ( tcp ) ;
}
break ;
2000-09-02 01:03:06 +04:00
# ifdef FREEBSD
case 0 : /* handle case we polled for nothing */
continue ;
2002-12-16 23:40:39 +03:00
# endif
1999-02-19 03:21:36 +03:00
default :
1999-08-30 03:15:07 +04:00
fprintf ( stderr , " odd stop %d \n " , tcp - > status . PR_WHY ) ;
1999-02-19 03:21:36 +03:00
exit ( 1 ) ;
break ;
}
1999-08-30 03:15:07 +04:00
arg = 0 ;
2002-12-16 23:40:39 +03:00
# ifndef FREEBSD
1999-08-30 03:15:07 +04:00
if ( IOCTL ( tcp - > pfd , PIOCRUN , & arg ) < 0 ) {
2002-12-16 23:40:39 +03:00
# else
2000-09-02 01:03:06 +04:00
if ( IOCTL ( tcp - > pfd , PIOCRUN , 0 ) < 0 ) {
2002-12-16 23:40:39 +03:00
# endif
1999-02-19 03:21:36 +03:00
perror ( " PIOCRUN " ) ;
exit ( 1 ) ;
}
}
return 0 ;
}
2000-09-02 01:03:06 +04:00
# else /* !USE_PROCFS */
1999-02-19 03:21:36 +03:00
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
# ifdef TCB_GROUP_EXITING
/* Handle an exit detach or death signal that is taking all the
related clone threads with it . This is called in three circumstances :
SIG = = - 1 TCP has already died ( TCB_ATTACHED is clear , strace is parent ) .
SIG = = 0 Continuing TCP will perform an exit_group syscall .
SIG = = other Continuing TCP with SIG will kill the process .
*/
static int
handle_group_exit ( struct tcb * tcp , int sig )
{
/* We need to locate our records of all the clone threads
related to TCP , either its children or siblings . */
struct tcb * leader = ( ( tcp - > flags & TCB_CLONE_THREAD )
? tcp - > parent
: tcp - > nclone_detached > 0
? tcp : NULL ) ;
if ( sig < 0 ) {
if ( leader ! = NULL & & leader ! = tcp )
fprintf ( stderr ,
" PANIC: handle_group_exit: %d leader %d \n " ,
tcp - > pid , leader ? leader - > pid : - 1 ) ;
droptcb ( tcp ) ; /* Already died. */
}
else {
if ( tcp - > flags & TCB_ATTACHED ) {
if ( leader ! = NULL & & leader ! = tcp ) {
/* We need to detach the leader so that the
process death will be reported to its real
parent . But we kill it first to prevent
it doing anything before we kill the whole
process in a moment . We can use
PTRACE_KILL on a thread that ' s not already
stopped . Then the value we pass in
PTRACE_DETACH just sets the death
signal reported to the real parent . */
ptrace ( PTRACE_KILL , leader - > pid , 0 , 0 ) ;
if ( debug )
fprintf ( stderr ,
" [%d exit %d kills %d] \n " ,
tcp - > pid , sig , leader - > pid ) ;
detach ( leader , sig ) ;
}
detach ( tcp , sig ) ;
}
else if ( ptrace ( PTRACE_CONT , tcp - > pid , ( char * ) 1 , sig ) < 0 ) {
perror ( " strace: ptrace(PTRACE_CONT, ...) " ) ;
cleanup ( ) ;
return - 1 ;
}
else {
if ( leader ! = NULL & & leader ! = tcp )
droptcb ( tcp ) ;
/* The leader will report to us as parent now,
and then we ' ll get to the SIG = = - 1 case . */
return 0 ;
}
}
/* Note that TCP and LEADER are no longer valid,
but we can still compare against them . */
if ( leader ! = NULL ) {
unsigned int i ;
for ( i = 0 ; i < tcbtabsize ; i + + ) {
struct tcb * t = tcbtab [ i ] ;
if ( t ! = tcp & & ( t - > flags & TCB_CLONE_DETACHED )
& & t - > parent = = leader )
droptcb ( t ) ;
}
}
return 0 ;
}
# endif
1999-02-19 03:21:36 +03:00
static int
trace ( )
{
int pid ;
int wait_errno ;
int status ;
struct tcb * tcp ;
# ifdef LINUX
struct rusage ru ;
2001-03-28 18:40:14 +04:00
# ifdef __WALL
static int wait4_options = __WALL ;
# endif
1999-02-19 03:21:36 +03:00
# endif /* LINUX */
while ( nprocs ! = 0 ) {
if ( interactive )
sigprocmask ( SIG_SETMASK , & empty_set , NULL ) ;
# ifdef LINUX
2001-03-28 18:40:14 +04:00
# ifdef __WALL
pid = wait4 ( - 1 , & status , wait4_options , cflag ? & ru : NULL ) ;
2002-12-17 07:50:47 +03:00
if ( pid < 0 & & ( wait4_options & __WALL ) & & errno = = EINVAL ) {
2001-03-28 18:40:14 +04:00
/* this kernel does not support __WALL */
wait4_options & = ~ __WALL ;
errno = 0 ;
pid = wait4 ( - 1 , & status , wait4_options ,
cflag ? & ru : NULL ) ;
}
2002-12-17 07:50:47 +03:00
if ( pid < 0 & & ! ( wait4_options & __WALL ) & & errno = = ECHILD ) {
2001-03-28 18:40:14 +04:00
/* most likely a "cloned" process */
pid = wait4 ( - 1 , & status , __WCLONE ,
cflag ? & ru : NULL ) ;
if ( pid = = - 1 ) {
fprintf ( stderr , " strace: clone wait4 "
" failed: %s \n " , strerror ( errno ) ) ;
}
}
# else
1999-02-19 03:21:36 +03:00
pid = wait4 ( - 1 , & status , 0 , cflag ? & ru : NULL ) ;
2001-03-28 18:40:14 +04:00
# endif /* __WALL */
1999-02-19 03:21:36 +03:00
# endif /* LINUX */
# ifdef SUNOS4
pid = wait ( & status ) ;
# endif /* SUNOS4 */
wait_errno = errno ;
if ( interactive )
sigprocmask ( SIG_BLOCK , & blocked_set , NULL ) ;
if ( interrupted )
return 0 ;
if ( pid = = - 1 ) {
switch ( wait_errno ) {
case EINTR :
continue ;
case ECHILD :
/*
* We would like to verify this case
* but sometimes a race in Solbourne ' s
* version of SunOS sometimes reports
* ECHILD before sending us SIGCHILD .
*/
#if 0
if ( nprocs = = 0 )
return 0 ;
fprintf ( stderr , " strace: proc miscount \n " ) ;
exit ( 1 ) ;
# endif
return 0 ;
default :
errno = wait_errno ;
perror ( " strace: wait " ) ;
return - 1 ;
}
}
if ( debug )
fprintf ( stderr , " [wait(%#x) = %u] \n " , status , pid ) ;
/* Look up `pid' in our table. */
if ( ( tcp = pid2tcb ( pid ) ) = = NULL ) {
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
# ifdef LINUX
if ( followfork | | followvfork ) {
/* This is needed to go with the CLONE_PTRACE
changes in process . c / util . c : we might see
the child ' s initial trap before we see the
parent return from the clone syscall .
Leave the child suspended until the parent
returns from its system call . Only then
will we have the association of parent and
child so that we know how to do clearbpt
in the child . */
if ( ( tcp = alloctcb ( pid ) ) = = NULL ) {
fprintf ( stderr , " [tcb table full] \n " ) ;
kill ( pid , SIGKILL ) ; /* XXX */
return 0 ;
}
tcp - > flags | = TCB_ATTACHED | TCB_SUSPENDED ;
newoutf ( tcp ) ;
if ( ! qflag )
fprintf ( stderr , " \
Process % d attached ( waiting for parent ) \ n " ,
pid ) ;
2000-02-04 00:58:30 +03:00
}
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
else
/* This can happen if a clone call used
CLONE_PTRACE itself . */
2000-02-04 00:58:30 +03:00
# endif
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
{
fprintf ( stderr , " unknown pid: %u \n " , pid ) ;
if ( WIFSTOPPED ( status ) )
ptrace ( PTRACE_CONT , pid , ( char * ) 1 , 0 ) ;
exit ( 1 ) ;
}
1999-02-19 03:21:36 +03:00
}
/* set current output file */
outf = tcp - > outf ;
if ( cflag ) {
# ifdef LINUX
tv_sub ( & tcp - > dtime , & ru . ru_stime , & tcp - > stime ) ;
tcp - > stime = ru . ru_stime ;
# endif /* !LINUX */
}
if ( tcp - > flags & TCB_SUSPENDED ) {
/*
* Apparently , doing any ptrace ( ) call on a stopped
* process , provokes the kernel to report the process
* status again on a subsequent wait ( ) , even if the
* process has not been actually restarted .
* Since we have inspected the arguments of suspended
* processes we end up here testing for this case .
*/
continue ;
}
if ( WIFSIGNALED ( status ) ) {
if ( ! cflag
& & ( qual_flags [ WTERMSIG ( status ) ] & QUAL_SIGNAL ) ) {
printleader ( tcp ) ;
tprintf ( " +++ killed by %s +++ " ,
1999-03-30 03:23:13 +04:00
signame ( WTERMSIG ( status ) ) ) ;
1999-02-19 03:21:36 +03:00
printtrailer ( tcp ) ;
}
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
# ifdef TCB_GROUP_EXITING
handle_group_exit ( tcp , - 1 ) ;
# else
1999-02-19 03:21:36 +03:00
droptcb ( tcp ) ;
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
# endif
1999-02-19 03:21:36 +03:00
continue ;
}
if ( WIFEXITED ( status ) ) {
if ( debug )
fprintf ( stderr , " pid %u exited \n " , pid ) ;
if ( tcp - > flags & TCB_ATTACHED )
fprintf ( stderr ,
" PANIC: attached pid %u exited \n " ,
pid ) ;
2003-06-10 07:05:53 +04:00
if ( tcp = = tcp_last ) {
if ( ( tcp - > flags & ( TCB_INSYSCALL | TCB_REPRINT ) )
= = TCB_INSYSCALL )
tprintf ( " <unfinished ... exit status %d> \n " ,
WEXITSTATUS ( status ) ) ;
tcp_last = NULL ;
}
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
# ifdef TCB_GROUP_EXITING
handle_group_exit ( tcp , - 1 ) ;
# else
1999-02-19 03:21:36 +03:00
droptcb ( tcp ) ;
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
# endif
1999-02-19 03:21:36 +03:00
continue ;
}
if ( ! WIFSTOPPED ( status ) ) {
fprintf ( stderr , " PANIC: pid %u not stopped \n " , pid ) ;
droptcb ( tcp ) ;
continue ;
}
if ( debug )
fprintf ( stderr , " pid %u stopped, [%s] \n " ,
1999-03-30 03:23:13 +04:00
pid , signame ( WSTOPSIG ( status ) ) ) ;
1999-02-19 03:21:36 +03:00
if ( tcp - > flags & TCB_STARTUP ) {
/*
* This flag is there to keep us in sync .
* Next time this process stops it should
* really be entering a system call .
*/
tcp - > flags & = ~ TCB_STARTUP ;
if ( tcp - > flags & TCB_ATTACHED ) {
/*
* Interestingly , the process may stop
* with STOPSIG equal to some other signal
* than SIGSTOP if we happend to attach
* just before the process takes a signal .
*/
if ( ! WIFSTOPPED ( status ) ) {
fprintf ( stderr ,
" pid %u not stopped \n " , pid ) ;
detach ( tcp , WSTOPSIG ( status ) ) ;
continue ;
}
}
else {
# ifdef SUNOS4
/* A child of us stopped at exec */
if ( WSTOPSIG ( status ) = = SIGTRAP & & followvfork )
fixvfork ( tcp ) ;
# endif /* SUNOS4 */
}
if ( tcp - > flags & TCB_BPTSET ) {
if ( clearbpt ( tcp ) < 0 ) /* Pretty fatal */ {
droptcb ( tcp ) ;
cleanup ( ) ;
return - 1 ;
}
}
goto tracing ;
}
if ( WSTOPSIG ( status ) ! = SIGTRAP ) {
if ( WSTOPSIG ( status ) = = SIGSTOP & &
( tcp - > flags & TCB_SIGTRAPPED ) ) {
/*
* Trapped attempt to block SIGTRAP
* Hope we are back in control now .
*/
tcp - > flags & = ~ ( TCB_INSYSCALL | TCB_SIGTRAPPED ) ;
if ( ptrace ( PTRACE_SYSCALL ,
pid , ( char * ) 1 , 0 ) < 0 ) {
perror ( " trace: ptrace(PTRACE_SYSCALL, ...) " ) ;
cleanup ( ) ;
return - 1 ;
}
continue ;
}
if ( ! cflag
& & ( qual_flags [ WSTOPSIG ( status ) ] & QUAL_SIGNAL ) ) {
2001-10-10 03:47:38 +04:00
unsigned long addr = 0 , pc = 0 ;
# ifdef PT_GETSIGINFO
# define PSR_RI 41
struct siginfo si ;
unsigned long psr ;
upeek ( pid , PT_CR_IPSR , & psr ) ;
upeek ( pid , PT_CR_IIP , & pc ) ;
pc + = ( psr > > PSR_RI ) & 0x3 ;
ptrace ( PT_GETSIGINFO , pid , 0 , ( long ) & si ) ;
addr = ( unsigned long ) si . si_addr ;
# endif
1999-02-19 03:21:36 +03:00
printleader ( tcp ) ;
2001-10-10 03:47:38 +04:00
tprintf ( " --- %s (%s) @ %lx (%lx) --- " ,
1999-03-30 03:23:13 +04:00
signame ( WSTOPSIG ( status ) ) ,
2001-10-10 03:47:38 +04:00
strsignal ( WSTOPSIG ( status ) ) , pc , addr ) ;
1999-02-19 03:21:36 +03:00
printtrailer ( tcp ) ;
}
if ( ( tcp - > flags & TCB_ATTACHED ) & &
! sigishandled ( tcp , WSTOPSIG ( status ) ) ) {
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
# ifdef TCB_GROUP_EXITING
handle_group_exit ( tcp , WSTOPSIG ( status ) ) ;
# else
1999-02-19 03:21:36 +03:00
detach ( tcp , WSTOPSIG ( status ) ) ;
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
# endif
1999-02-19 03:21:36 +03:00
continue ;
}
if ( ptrace ( PTRACE_SYSCALL , pid , ( char * ) 1 ,
WSTOPSIG ( status ) ) < 0 ) {
perror ( " trace: ptrace(PTRACE_SYSCALL, ...) " ) ;
cleanup ( ) ;
return - 1 ;
}
tcp - > flags & = ~ TCB_SUSPENDED ;
continue ;
}
if ( trace_syscall ( tcp ) < 0 ) {
if ( tcp - > flags & TCB_ATTACHED )
detach ( tcp , 0 ) ;
else {
ptrace ( PTRACE_KILL ,
tcp - > pid , ( char * ) 1 , SIGTERM ) ;
droptcb ( tcp ) ;
}
continue ;
}
if ( tcp - > flags & TCB_EXITING ) {
2003-01-08 Roland McGrath <roland@redhat.com>
Support for new Linux 2.5 thread features.
* defs.h [LINUX]: Define __NR_exit_group if not defined.
(struct tcb): New members nclone_threads, nclone_detached,
and nclone_waiting.
(TCB_CLONE_DETACHED, TCB_CLONE_THREAD, TCB_GROUP_EXITING): New macros.
(waiting_parent): Macro removed.
(pid2tcb): Declare it.
* process.c (internal_clone) [TCB_CLONE_THREAD]: Reparent the new
child to our parent if we are a CLONE_THREAD child ourselves.
Maintain TCB_CLONE_THREAD and TCB_CLONE_DETACHED flags and counts.
(internal_wait) [TCB_CLONE_THREAD]: Factor out detached children when
determining if we have any. If TCB_CLONE_THREAD is set, check
parent's children instead of our own, and bump nclone_waiting count.
(internal_exit) [__NR_exit_group]: Set the TCB_GROUP_EXITING flag if
the syscall was exit_group.
* syscall.c (internal_syscall): Use internal_exit for exit_group.
* strace.c (pid2tcb): No longer static.
(alloctcb) [TCB_CLONE_THREAD]: Initialize new fields.
(droptcb) [TCB_CLONE_THREAD]: Maintain new fields.
If we have thread children, set TCB_EXITING and don't clear the TCB.
(resume) [TCB_CLONE_THREAD]: Decrement parent's nclone_waiting.
(detach) [TCB_CLONE_THREAD]: When calling resume, check all thread
children of our parent that might be waiting for us too.
[TCB_GROUP_EXITING] (handle_group_exit): New function.
(trace) [TCB_GROUP_EXITING]: Use that in place of detach or droptcb.
Revamp -f support for Linux.
* util.c [LINUX] (setbpt, clearbpt): New implementations that tweak
the system call to be clone with CLONE_PTRACE set. Various new static
helper functions.
* process.c (internal_clone): Define also #ifdef SYS_clone2.
Initialize TCPCHILD->parent field.
[CLONE_PTRACE]: Don't do PTRACE_ATTACH here, because it's preattached.
Check in case the new child is in the tcb already.
(internal_fork) [LINUX]: Just call internal_clone.
* strace.c (trace) [LINUX]: Under -f/-F, grok an unknown pid
reporting to wait, put it in the TCB with TCB_ATTACHED|TCB_SUSPENDED.
2003-01-09 09:53:31 +03:00
# ifdef TCB_GROUP_EXITING
if ( tcp - > flags & TCB_GROUP_EXITING ) {
if ( handle_group_exit ( tcp , 0 ) < 0 )
return - 1 ;
continue ;
}
# endif
1999-02-19 03:21:36 +03:00
if ( tcp - > flags & TCB_ATTACHED )
detach ( tcp , 0 ) ;
else if ( ptrace ( PTRACE_CONT , pid , ( char * ) 1 , 0 ) < 0 ) {
perror ( " strace: ptrace(PTRACE_CONT, ...) " ) ;
cleanup ( ) ;
return - 1 ;
}
continue ;
}
if ( tcp - > flags & TCB_SUSPENDED ) {
if ( ! qflag )
fprintf ( stderr , " Process %u suspended \n " , pid ) ;
continue ;
}
tracing :
if ( ptrace ( PTRACE_SYSCALL , pid , ( char * ) 1 , 0 ) < 0 ) {
perror ( " trace: ptrace(PTRACE_SYSCALL, ...) " ) ;
cleanup ( ) ;
return - 1 ;
}
}
return 0 ;
}
2000-09-02 01:03:06 +04:00
# endif /* !USE_PROCFS */
1999-02-19 03:21:36 +03:00
static int curcol ;
# ifdef __STDC__
# include <stdarg.h>
# define VA_START(a, b) va_start(a, b)
# else
# include <varargs.h>
# define VA_START(a, b) va_start(a)
# endif
void
# ifdef __STDC__
tprintf ( const char * fmt , . . . )
# else
tprintf ( fmt , va_alist )
char * fmt ;
va_dcl
# endif
{
va_list args ;
VA_START ( args , fmt ) ;
2003-11-07 02:41:22 +03:00
if ( outf ) {
int n = vfprintf ( outf , fmt , args ) ;
if ( n < 0 & & outf ! = stderr )
perror ( outfname = = NULL
? " <writing to pipe> " : outfname ) ;
else
curcol + = n ;
}
1999-02-19 03:21:36 +03:00
va_end ( args ) ;
return ;
}
void
printleader ( tcp )
struct tcb * tcp ;
{
if ( tcp_last & & ( ! outfname | | followfork < 2 | | tcp_last = = tcp ) ) {
tcp_last - > flags | = TCB_REPRINT ;
tprintf ( " <unfinished ...> \n " ) ;
}
curcol = 0 ;
if ( ( followfork = = 1 | | pflag_seen > 1 ) & & outfname )
tprintf ( " %-5d " , tcp - > pid ) ;
else if ( nprocs > 1 & & ! outfname )
tprintf ( " [pid %5u] " , tcp - > pid ) ;
if ( tflag ) {
char str [ sizeof ( " HH:MM:SS " ) ] ;
struct timeval tv , dtv ;
static struct timeval otv ;
gettimeofday ( & tv , NULL ) ;
if ( rflag ) {
if ( otv . tv_sec = = 0 )
otv = tv ;
tv_sub ( & dtv , & tv , & otv ) ;
tprintf ( " %6ld.%06ld " ,
( long ) dtv . tv_sec , ( long ) dtv . tv_usec ) ;
otv = tv ;
}
else if ( tflag > 2 ) {
tprintf ( " %ld.%06ld " ,
( long ) tv . tv_sec , ( long ) tv . tv_usec ) ;
}
else {
time_t local = tv . tv_sec ;
strftime ( str , sizeof ( str ) , " %T " , localtime ( & local ) ) ;
if ( tflag > 1 )
tprintf ( " %s.%06ld " , str , ( long ) tv . tv_usec ) ;
else
tprintf ( " %s " , str ) ;
}
}
if ( iflag )
printcall ( tcp ) ;
}
void
tabto ( col )
int col ;
{
if ( curcol < col )
tprintf ( " %*s " , col - curcol , " " ) ;
}
void
printtrailer ( tcp )
struct tcb * tcp ;
{
tprintf ( " \n " ) ;
tcp_last = NULL ;
}
1999-08-30 03:15:07 +04:00
1999-11-29 18:34:02 +03:00
# ifdef HAVE_MP_PROCFS
1999-08-30 03:15:07 +04:00
int mp_ioctl ( int fd , int cmd , void * arg , int size ) {
struct iovec iov [ 2 ] ;
int n = 1 ;
2002-12-16 23:40:39 +03:00
1999-08-30 03:15:07 +04:00
iov [ 0 ] . iov_base = & cmd ;
iov [ 0 ] . iov_len = sizeof cmd ;
if ( arg ) {
+ + n ;
iov [ 1 ] . iov_base = arg ;
iov [ 1 ] . iov_len = size ;
}
2002-12-16 23:40:39 +03:00
1999-08-30 03:15:07 +04:00
return writev ( fd , iov , n ) ;
}
# endif